├── MQL5 ├── MQL5 ├── py_json_csv_0204.ex5 ├── py_json_csv_0204.mq5 ├── readJson.ex5 └── readJson.mq5 ├── README.md ├── actionWriter.py ├── main_DecisionMaker.py ├── output.py ├── sma_ema.py └── trading_strategies ├── adx_crossover.py ├── adx_ema_14.py ├── adx_rsi.py ├── aroon_adx.py ├── aroon_indicator.py ├── aroon_solo.py ├── awesome_saucer.py ├── awesome_zero_crossover.py ├── blade_runner.py ├── bollingerbands_rsi.py ├── bollingerbands_rsi_2.py ├── cci_macd_psar.py ├── cci_moving_average.py ├── commodity_channel_index.py ├── donchian_atr.py ├── donchian_breakout.py ├── donchian_middle.py ├── dpo_candlestick.py ├── elder_ray.py ├── elder_ray_alternative.py ├── elder_ray_sma.py ├── ema_3.py ├── ema_3_alternative.py ├── ema_crossover.py ├── ema_crossover_alternative.py ├── ema_crossover_macd.py ├── ema_crossover_rsi.py ├── ema_crossover_rsi_alternative.py ├── ema_macd_rsi.py ├── ema_mi.py ├── force_index_ema.py ├── ichimoku_cloud_psar.py ├── k_stoch_adx.py ├── kama.py ├── kama_crossover.py ├── keltner_adx.py ├── keltner_rsi.py ├── keltner_stochastic.py ├── macd_crossover.py ├── macd_histogram_reversal.py ├── macd_rsi_sma.py ├── macd_stochastic_crossover.py ├── macd_zero_cross.py ├── mfi.py ├── mfi_stochastic.py ├── oops_signals.py ├── preprocessor.py ├── psar_moving_average.py ├── rsi_2.py ├── rsi_80_20.py ├── sma_ema.py ├── sma_ema_alternative.py ├── sma_mi.py ├── stochastic_oscillator_no_exit.py ├── trading_strategies ├── triple_bollingerbands.py ├── trix_ema.py ├── trix_mi.py ├── trix_rsi.py ├── tsi_crossover.py ├── visualise.py ├── vortex_crossover.py ├── vortex_sma.py ├── williams_r_sma.py ├── williams_rsi.py ├── williams_stochastic.py └── zig_zag.py /MQL5/MQL5: -------------------------------------------------------------------------------- 1 | MQL5 Expert advisers 2 | Add the EA into 'EA' folder of the MQL5 3 | -------------------------------------------------------------------------------- /MQL5/py_json_csv_0204.ex5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkhushi/MQL5-Python-Backtesting/4bd7f9996377e78ff6af6ce0517885e3978d57fe/MQL5/py_json_csv_0204.ex5 -------------------------------------------------------------------------------- /MQL5/py_json_csv_0204.mq5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkhushi/MQL5-Python-Backtesting/4bd7f9996377e78ff6af6ce0517885e3978d57fe/MQL5/py_json_csv_0204.mq5 -------------------------------------------------------------------------------- /MQL5/readJson.ex5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkhushi/MQL5-Python-Backtesting/4bd7f9996377e78ff6af6ce0517885e3978d57fe/MQL5/readJson.ex5 -------------------------------------------------------------------------------- /MQL5/readJson.mq5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkhushi/MQL5-Python-Backtesting/4bd7f9996377e78ff6af6ce0517885e3978d57fe/MQL5/readJson.mq5 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MQL5-Python-Backtesting 2 | MQL5 based backtesting using python 3 | 4 | 5 | Trading strategies send the buy, sell or hold signal to MQL5 6 | Based on the signal Metatrader 5 perform the trading 7 | 8 | How to run the Strategies: 9 | 1. open main_decisionmaker.py and import the trading strategy which is intended to run 10 | 2. Update the strategy and the signal line 11 | 3. Start the backtesting in Metatrader 5 12 | 4. Copy the python files along with the strategy folder(trading_strategies) into Metatrader 5 testing/files folder 13 | 5. copy other python files - main_decisionmaker.py, output.py and actionWriter.py 14 | 6. run main_decisionmaker.py 15 | 16 | Explained in this video https://www.youtube.com/watch?v=ovKgQdiQsHE&t=764s 17 | -------------------------------------------------------------------------------- /actionWriter.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | import time 3 | import os 4 | import copy, sys 5 | import pandas as pd 6 | import numpy as np 7 | import talib 8 | import json 9 | from output import output 10 | 11 | class actionWriter(): 12 | def __init__(self, trading_algrithm): 13 | self.trading_algrithm = trading_algrithm 14 | 15 | def write_strategies(self, data): 16 | with open('action_test.txt', 'w') as outfile: 17 | json.dump(data, outfile) 18 | 19 | def save2csv(self,output_save, predict_result, contents, signal, prev_signal, df): 20 | output_save.save_csv(contents, df, signal, prev_signal, predict_result) 21 | 22 | 23 | 24 | def cleanFile(self, filename): 25 | del_f = open(filename, "w") 26 | del_f.close() 27 | 28 | 29 | def run(self): 30 | filename = "time_close_csv_test.csv" 31 | pre_Timebar = 0 32 | output_save = output() 33 | check_point = 0 34 | 35 | if os.path.isfile(filename) and os.stat(filename).st_size != 0: 36 | print("File exist and not empty") 37 | 38 | while True: 39 | if os.stat(filename).st_size != 0: 40 | try: 41 | with open(filename, encoding='utf-16') as f: 42 | contents = f.read() 43 | # you may also want to remove whitespace characters like `\n` at the end of each line 44 | contents = contents.splitlines() 45 | contents = [x.split('\t') for x in contents] 46 | for i in range(len(contents)): 47 | contents[i][0] = datetime.strptime(contents[i][0], '%Y.%m.%d %H:%M:%S') 48 | contents[i][1] = float(contents[i][1]) #open 49 | contents[i][2] = float(contents[i][2]) #high 50 | contents[i][3] = float(contents[i][3]) #low 51 | contents[i][4] = float(contents[i][4]) #close 52 | contents[i][5] = int(contents[i][5]) #tick value 53 | 54 | newTimebar = contents[-1][0] 55 | curr_position = contents[-1][-1] 56 | curr_close_price = contents[-1][4] 57 | if curr_position == "Ending": 58 | 59 | print(">>>------------------------<<<") 60 | output_save.output_csv() 61 | print(">>> Server Stop <<<") 62 | break 63 | 64 | 65 | else: 66 | if pre_Timebar != newTimebar: 67 | pre_Timebar = copy.deepcopy(newTimebar) 68 | 69 | print("Timebar: ",pre_Timebar) 70 | print("curr_close_price: ",curr_close_price) 71 | print("curr_position", curr_position) 72 | 73 | # code from example2.py, send the data to the main_DecisionMaker.py 74 | predict_result, signal, prev_signal, df = self.trading_algrithm.predict(contents) 75 | if type(predict_result) is not dict: 76 | raise ValueError("Value must return a dictionary type") 77 | print("predict_result","\t",predict_result) 78 | 79 | # write the result to txt or csv 80 | self.write_strategies(predict_result) 81 | # self.cleanFile(filename) 82 | 83 | self.save2csv(output_save, predict_result, contents, signal, prev_signal, df) 84 | 85 | check_point += 1 86 | 87 | if check_point % 50 == 0: 88 | output_save.output_csv() 89 | 90 | else: 91 | time.sleep(0.003) 92 | 93 | except : 94 | continue 95 | 96 | else: 97 | # print("File is empty") 98 | time.sleep(0.001) 99 | else: 100 | print("File not exist") -------------------------------------------------------------------------------- /output.py: -------------------------------------------------------------------------------- 1 | 2 | import pandas as pd 3 | 4 | class output(): 5 | def __init__(self): 6 | self.date_lst = [] 7 | self.close_lst = [] 8 | self.signal_lst = [] 9 | self.prev_signal_lst = [] 10 | self.action_lst = [] 11 | 12 | 13 | def save_csv(self, contents, dframe, signal, prev_signal, predict_result): 14 | date = contents[-1][0] 15 | close = dframe['close'].iloc[-1] 16 | 17 | 18 | self.date_lst.append(date) 19 | self.close_lst.append(close) 20 | self.signal_lst.append(signal) 21 | self.prev_signal_lst.append(prev_signal) 22 | self.action_lst.append(predict_result["action"]) 23 | 24 | 25 | def output_csv(self): 26 | output_lst = [self.date_lst, self.close_lst, self.signal_lst, self.prev_signal_lst, self.action_lst] 27 | df_output = pd.DataFrame(output_lst).transpose() 28 | df_output.columns=['date','close_price', 'signal','prev_signal','action'] 29 | df_output.to_csv("output.csv", index=False) 30 | -------------------------------------------------------------------------------- /sma_ema.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import trading_strategies.visualise as v 3 | 4 | # @author: vita 5 | 6 | # This strategy uses a 5 day simple movinng average (SMA) for sell and buy signals and a 7 | # 144 period and 169 period exponential moving average (EMA) to determine trend direction 8 | 9 | class SimpleMAExponentialMA: 10 | def __init__(self, file_path): 11 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos")) 12 | self.close = self.df['close'] # retrieves the most recent closing price 13 | 14 | def calculate_144ema(self): 15 | self.df['144ema'] = self.df['close'].ewm(span=144, min_periods=144, adjust=False).mean() 16 | 17 | def calculate_169ema(self): 18 | self.df['169ema'] = self.df['close'].ewm(span=169, min_periods=169, adjust=False).mean() 19 | 20 | def calculate_5sma(self): 21 | self.df['5sma'] = self.df['close'].rolling(window=5).mean() 22 | 23 | def determine_signal(self, dframe): 24 | action = 0 # hold 25 | 26 | close = dframe['close'] 27 | ema_144 = dframe['144ema'] 28 | ema_169 = dframe['169ema'] 29 | sma_5 = dframe['5sma'] 30 | 31 | # SELL CRITERIA: if closing price is below SMA and 169-period EMA is above 144-period EMA 32 | if (close.iloc[-1] < sma_5.iloc[-1]) and (ema_169.iloc[-1] > ema_144.iloc[-1]): 33 | action = -1 34 | # BUY CRITERIA: closing price is above SMA and 144-period EMA is above 169-period EMA 35 | elif (close.iloc[-1] > sma_5.iloc[-1]) and (ema_144.iloc[-1] > ema_169.iloc[-1]): 36 | action = 1 37 | 38 | return action, ema_144.iloc[-1] - ema_169.iloc[-1], 39 | 40 | def run_sma_ema(self): 41 | self.calculate_144ema() 42 | self.calculate_169ema() 43 | self.calculate_5sma() 44 | signal = self.determine_signal(self.df) 45 | 46 | return signal, self.df 47 | 48 | ''' The following methods are for plotting ''' 49 | 50 | def find_all_signals(self, plot_df): 51 | # assign initial value of hold 52 | plot_df['signal'] = 0 53 | 54 | start = -1 * len(plot_df) 55 | end = start + 169 56 | 57 | # loop through data to determine all signals 58 | while end < 0: 59 | curr_window = plot_df[start:end] 60 | action = self.determine_signal(curr_window)[0] 61 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 62 | end += 1 63 | start += 1 64 | 65 | action = self.determine_signal(plot_df[-169:])[0] 66 | plot_df.loc[plot_df.index[-1], 'signal'] = action 67 | 68 | def plot_graph(self): 69 | # create shallow copy for plotting so as to not accidentally impact original df 70 | plot_df = self.df.copy(deep=False) 71 | 72 | self.find_all_signals(plot_df) 73 | 74 | # initialise visualisation object for plotting 75 | visualisation = v.Visualise(plot_df) 76 | 77 | # determining one buy signal example for plotting 78 | visualisation.determine_buy_marker() 79 | 80 | # determining one sell signal example for plotting 81 | visualisation.determine_sell_marker() 82 | 83 | # add subplots 84 | visualisation.add_subplot(plot_df['144ema'], color='turquoise', width=0.75) 85 | visualisation.add_subplot(plot_df['169ema'], color='violet', width=0.75) 86 | visualisation.add_subplot(plot_df['5sma'], color='orange', width=0.75) 87 | 88 | # create final plot with title 89 | visualisation.plot_graph("Simple and Exponential Moving Averages Strategy") 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /trading_strategies/adx_crossover.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Sep 27 09:29:36 2020 4 | 5 | @author: mingyu 6 | """ 7 | 8 | import pandas as pd 9 | import ta 10 | #import talib as ta 11 | import trading_strategies.visualise as v 12 | 13 | class AdxCrossover: 14 | def __init__(self, file_path): 15 | #self.df = pd.read_csv(file_path) 16 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos")) 17 | self.high = self.df['high'] 18 | self.low = self.df['low'] 19 | self.close = self.df['close'] 20 | 21 | def calculate_adx(self): 22 | self.df['adx'] = ta.trend.ADXIndicator(high = self.high, low = self.low, close = self.close, window = 20).adx() 23 | 24 | def calculate_minus_DI(self): 25 | self.df['-di'] = ta.trend.ADXIndicator(high = self.high, low = self.low, close = self.close, window = 20).adx_neg() 26 | 27 | 28 | def calculate_plus_DI(self): 29 | self.df['+di'] = ta.trend.ADXIndicator(high = self.high, low = self.low, close = self.close, window = 20).adx_pos() 30 | 31 | 32 | def determine_signal(self, dframe): 33 | 34 | # initialise signal to hold: 0 35 | signal = 0 36 | 37 | # BUY SIGNAL: adx is above 25 and the positive DI crosses over negative DI indicates a strong uptrend 38 | if dframe['adx'].iloc[-1] > 25 and dframe['+di'].iloc[-1] > dframe['-di'].iloc[-1] and dframe['+di'].iloc[-2] <= dframe['-di'].iloc[-2]: 39 | signal = 1 40 | # SELL SIGNAL: adx is above 25 and the negative DI crosses over positive DI indicates a strong downtrend 41 | elif dframe['adx'].iloc[-1] > 25 and dframe['+di'].iloc[-1] < dframe['-di'].iloc[-1] and dframe['+di'].iloc[-2] >= dframe['-di'].iloc[-2]: 42 | signal = -1 43 | 44 | return (signal, dframe['close'].iloc[-1]) 45 | 46 | def run(self): 47 | self.calculate_adx() 48 | self.calculate_minus_DI() 49 | self.calculate_plus_DI() 50 | signal = self.determine_signal(self.df) 51 | return signal, self.df 52 | 53 | ''' The following methods are for plotting ''' 54 | 55 | def find_all_signals(self, plot_df): 56 | # assign intitial value of hold 57 | plot_df['signal'] = 0 58 | 59 | start = -1 * len( 60 | plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 61 | end = start + 40 # where the current window will stop (exclusive of the element at this index) 62 | 63 | # loop through data to determine all signals 64 | while end < 0: 65 | curr_window = plot_df[start:end] 66 | action = self.determine_signal(curr_window)[0] 67 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 68 | additional_info = self.determine_signal(curr_window)[1] 69 | plot_df.loc[plot_df.index[end - 1], 'additional_info'] = additional_info 70 | end += 1 71 | start += 1 72 | 73 | # compute final signal 74 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-40:])[0] 75 | 76 | def plot_graph(self): 77 | 78 | # deep copy of data so original is not impacted 79 | plot_df = self.df.copy(deep=True) 80 | 81 | # determine all signals for the dataset 82 | self.find_all_signals(plot_df) 83 | 84 | plot_df['zero_line'] = 0 85 | plot_df['25_line'] = 25 86 | 87 | # initialise visualisation object for plotting 88 | visualisation = v.Visualise(plot_df) 89 | 90 | # determining one buy signal example for plotting 91 | visualisation.determine_buy_marker() 92 | 93 | # determining one sell signal example for plotting 94 | visualisation.determine_sell_marker() 95 | 96 | # add subplots of senkou span A and B to form the ichimoku cloud, and the parabolic sar dots 97 | visualisation.add_subplot(plot_df['adx'], panel=1, color='blue', width=0.75, ylabel='ADX') 98 | visualisation.add_subplot(plot_df['25_line'], panel=1, color='k', secondary_y=False, width=0.75, linestyle='solid') 99 | visualisation.add_subplot(plot_df['+di'], panel=1, color='green', width=0.75) 100 | visualisation.add_subplot(plot_df['-di'], panel=1, color='red', width=0.75) 101 | 102 | # create final plot with title 103 | visualisation.plot_graph("ADX Crossover Strategy") 104 | -------------------------------------------------------------------------------- /trading_strategies/adx_ema_14.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import numpy as np 4 | import trading_strategies.visualise as v 5 | ''' 6 | ### Author:Vita ### 7 | Strategy from: 8 | https://forextester.com/blog/adx-14-ema-strategy 9 | This strategy uses ADX and 14EMA for buy and sell signals 10 | ''' 11 | 12 | 13 | class ADXEMA14: 14 | # constructor 15 | def __init__(self, file_path): 16 | self.df = pd.read_csv(file_path) 17 | self.close = self.df['close'] 18 | self.high = self.df['high'] 19 | self.low = self.df['low'] 20 | 21 | def add_adx(self): 22 | self.df['adx'] = ta.trend.ADXIndicator(high=self.high, low=self.low, close=self.close).adx() 23 | self.df['adx'].replace(0.0, np.nan, inplace=True) 24 | 25 | def calculate_ema_14(self): 26 | self.df['14ema'] = self.df['close'].ewm(span=14, adjust=False).mean() 27 | 28 | 29 | def determine_signal(self, dframe): 30 | 31 | adx = dframe['adx'] 32 | ema14 = dframe['14ema'] 33 | open = dframe['open'] 34 | close = dframe['close'] 35 | 36 | action = 0 37 | 38 | # SELL CRITERIA: if candlestick is bearish, close is less than 14 EMA and ADX indicator has crossed above 25: 39 | if (open.iloc[-1] > close.iloc[-1]) and (close.iloc[-1] < ema14.iloc[-1]) and (adx.iloc[-2] < 25 and adx.iloc[-1] > 25): 40 | action = -1 41 | # BUY CRITERIA: if candlestick is bullish, close is greater than 14EMA and ADX indicator has crossed above 25 42 | elif (close.iloc[-1] > open.iloc[-1]) and (close.iloc[-1] > ema14.iloc[-1]) and (adx.iloc[-2] < 25 and adx.iloc[-1] > 25): 43 | action = 1 44 | 45 | return action, dframe['adx'].iloc[-1], 46 | 47 | def run_adx_ema_14(self): 48 | self.add_adx() 49 | self.calculate_ema_14() 50 | return self.determine_signal(self.df), self.df 51 | 52 | ''' The following methods are for plotting ''' 53 | 54 | def find_all_signals(self, plot_df): 55 | # assign initial value of hold 56 | plot_df['signal'] = 0 57 | 58 | start = -1 * len(plot_df) 59 | end = start + 40 60 | 61 | # loop through data to determine all signals 62 | while end < 0: 63 | curr_window = plot_df[start:end] 64 | action = self.determine_signal(curr_window)[0] 65 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 66 | end += 1 67 | start += 1 68 | 69 | action = self.determine_signal(plot_df[-40:])[0] 70 | plot_df.loc[plot_df.index[-1], 'signal'] = action 71 | 72 | def plot_graph(self): 73 | # create shallow copy for plotting so as to not accidentally impact original df 74 | plot_df = self.df.copy(deep=False) 75 | 76 | self.find_all_signals(plot_df) 77 | 78 | plot_df['25_line'] = 25 79 | 80 | # initialise visualisation object for plotting 81 | visualisation = v.Visualise(plot_df) 82 | 83 | # determining one buy signal example for plotting 84 | visualisation.determine_buy_marker() 85 | 86 | # determining one sell signal example for plotting 87 | visualisation.determine_sell_marker() 88 | 89 | # add subplots 90 | visualisation.add_subplot(plot_df['adx'], panel=1, color='m', ylabel='adx') 91 | visualisation.add_subplot(plot_df['14ema'], color='violet', width=0.75) 92 | visualisation.add_subplot(plot_df['25_line'], panel=1, color='b', width=0.75, linestyle='solid') 93 | 94 | 95 | # create final plot with title 96 | visualisation.plot_graph("ADX 14-EMA Strategy") 97 | 98 | 99 | -------------------------------------------------------------------------------- /trading_strategies/adx_rsi.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import trading_strategies.visualise as v 4 | import numpy as np 5 | 6 | ''' 7 | This strategy uses the ADX indicator and RSI indicator to determine buy and sell signals. 8 | The Average Directional Movement Index (ADX) measures the strength of trends, while the 9 | Relative Strength Index is a momentum indicator measuring speed and change of price movements. 10 | The market can be considered oversold if RSI measures below 30 and overbought if it measures 11 | above 70. An ADX reading of over 20 indicates a strong trend while lower readings indicate a 12 | lack of trend. 13 | 14 | ''' 15 | 16 | class AdxRsi: 17 | def __init__(self, file_path): 18 | #self.max_window = 100 #uncomment for graphing purposes 19 | self.df = pd.read_csv(file_path) 20 | 21 | 22 | def calculate_adx(self): 23 | adx_ind = ta.trend.ADXIndicator(self.df['high'], self.df['low'], self.df['close'], n = 20) 24 | self.df['adx'] = adx_ind.adx() 25 | 26 | def calculate_rsi(self): 27 | rsi_ind = ta.momentum.RSIIndicator(self.df['close'], n = 2) 28 | self.df['rsi'] = rsi_ind.rsi() 29 | 30 | def determine_signal(self, dframe): 31 | 32 | # initialise all signals to hold: 0 33 | dframe['signal'] = 0 34 | 35 | # BUY SIGNAL: adx is above 20 and rsi reads below 30 implying oversold conditions 36 | dframe.loc[((dframe['adx'] > 20) & ((dframe['rsi'] <= 30) | (dframe['rsi'].shift(1) <= 30) | (dframe['rsi'].shift(2) <= 30) | (dframe['rsi'].shift(3) <= 30) | (dframe['rsi'].shift(4) <= 30))), 'signal'] = 1 37 | 38 | # SELL SIGNAL: adx is below 25 and rsi is showing overbought conditions, eg over 70 39 | dframe.loc[((dframe['adx'] > 20) & ((dframe['rsi'] >= 70) | (dframe['rsi'].shift(1) >= 70) | (dframe['rsi'].shift(2) >= 70) | (dframe['rsi'].shift(3) >= 70) | (dframe['rsi'].shift(4) >= 70))), 'signal'] = -1 40 | 41 | # return final data point's signal 42 | signal_col = dframe.columns.get_loc('signal') 43 | return(dframe.iloc[-1, signal_col], dframe['adx'].iloc[-1]) 44 | 45 | def run_adx_rsi(self): 46 | self.calculate_rsi() 47 | self.calculate_adx() 48 | signal = self.determine_signal(self.df) 49 | return signal, self.df 50 | 51 | ''' The following methods are for plotting ''' 52 | 53 | def find_all_signals(self, plot_df): 54 | # assign intitial value of hold 55 | plot_df['signal'] = 0 56 | 57 | start = -1 * len( 58 | plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 59 | end = start + 100 # where the current window will stop (exclusive of the element at this index) 60 | 61 | # loop through data to determine all signals 62 | while end < 0: 63 | curr_window = plot_df[start:end] 64 | action = self.determine_signal(curr_window)[0] 65 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 66 | end += 1 67 | start += 1 68 | 69 | # compute final signal 70 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-100:])[0] 71 | 72 | def plot_graph(self): 73 | # deep copy of data so original is not impacted 74 | plot_df = self.df.copy(deep=True) 75 | 76 | # determine all signals for the dataset 77 | self.find_all_signals(plot_df) 78 | 79 | # initialise visualisation object for plotting 80 | visualisation = v.Visualise(plot_df) 81 | 82 | # determining one buy signal example for plotting 83 | visualisation.determine_buy_marker() 84 | 85 | # determining one sell signal example for plotting 86 | visualisation.determine_sell_marker() 87 | 88 | line_30 = [30] * self.max_window 89 | line_70 = [70] * self.max_window 90 | line_20 = [20] * self.max_window 91 | 92 | # add subplots of 200ema and awesome oscillator 93 | visualisation.add_subplot(line_20, panel=1, color="orange") 94 | visualisation.add_subplot(plot_df['adx'], panel=1, ylabel="Adx") 95 | visualisation.add_subplot(plot_df['rsi'], panel=2, ylabel="Rsi") 96 | visualisation.add_subplot(line_30, panel=2, color="pink") 97 | visualisation.add_subplot(line_70, panel=2, color="blue") 98 | 99 | # create final plot with title 100 | visualisation.plot_graph("Adx Rsi Trading Strategy") -------------------------------------------------------------------------------- /trading_strategies/aroon_solo.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 4 | @author: caitlin 5 | The strategy uses the aroon indicator to determine buy and sell signals. 6 | The aroon up indicator shows how many periods since a 25 period high, and similarly the aroon down 7 | indicator shows how many periods since a 25 period low. Readings range from 0 to 100. Generally the 8 | market is bullish if aroon up > 50 and bearish if aroon down < 50. 9 | """ 10 | 11 | import pandas as pd 12 | import ta 13 | import trading_strategies.visualise as v 14 | 15 | 16 | class Aroon: 17 | 18 | # loading the data in from file_path 19 | def __init__(self, file_path): 20 | self.max_window = 100 # set to 100 to clearly see indicator working 21 | self.df = pd.read_csv(file_path)[-self.max_window:] 22 | self.high = self.df['high'] 23 | self.close = self.df['close'] 24 | self.low = self.df['low'] 25 | 26 | # Calculate the aroon up line 27 | def calculate_aroon(self): 28 | aroon_ind = ta.trend.AroonIndicator(close=self.close) 29 | self.df['aroon'] = aroon_ind.aroon_indicator() 30 | 31 | # Calculate the aroon up line 32 | def calculate_aroon_up(self): 33 | aroon_up = ta.trend.aroon_up(close=self.close) 34 | self.df['aroon_up'] = aroon_up 35 | 36 | # Calculate the aroon down line 37 | def calculate_aroon_down(self): 38 | aroon_down = ta.trend.aroon_down(close=self.close) 39 | self.df['aroon_down'] = aroon_down 40 | 41 | 42 | def determine_signal(self, dframe): 43 | dframe['signal'] = 0 44 | 45 | # A buy entry signal is given when aroon up > 50 and aroon down < 50 46 | dframe.loc[((((dframe['aroon_up'] > 50) & (dframe['aroon_down'] < 50)) | ((dframe['aroon_up'].shift(1) > 50) & (dframe['aroon_down'].shift(1) < 50)) 47 | | ((dframe['aroon_up'].shift(2) > 50) & (dframe['aroon_down'].shift(2) < 50)) | ((dframe['aroon_up'].shift(3) > 50) & (dframe['aroon_down'].shift(3) < 50)) 48 | | ((dframe['aroon_up'].shift(4) > 50) & (dframe['aroon_down'].shift(4) < 50)))), 'signal'] = 1 49 | 50 | 51 | # A sell entry signal is given when aroon down > 50 and aroon up < 50 52 | dframe.loc[( (((dframe['aroon_down'] > 50) & (dframe['aroon_up'] < 50)) | ((dframe['aroon_down'].shift(1) > 50) & (dframe['aroon_up'].shift(1) < 50)) 53 | | ((dframe['aroon_down'].shift(2) > 50) & (dframe['aroon_up'].shift(2) < 50)) | ((dframe['aroon_down'].shift(3) > 50) & (dframe['aroon_up'].shift(3) < 50)) 54 | | ((dframe['aroon_down'].shift(4) > 50) & (dframe['aroon_up'].shift(4) < 50)))), 'signal'] = -1 55 | 56 | 57 | 58 | signal_col = dframe.columns.get_loc('signal') 59 | return (dframe.iloc[-1, signal_col], dframe['aroon'].iloc[-1]) 60 | 61 | # Runs the indicator 62 | def run_aroon(self): 63 | self.calculate_aroon() 64 | self.calculate_aroon_up() 65 | self.calculate_aroon_down() 66 | 67 | signal = self.determine_signal(self.df) 68 | return signal, self.df 69 | 70 | ''' The following methods are for plotting ''' 71 | 72 | def find_all_signals(self, plot_df): 73 | # assign intitial value of hold 74 | plot_df['signal'] = 0 75 | 76 | start = -1*len(plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 77 | end = start + 100 # where the current window will stop (exclusive of the element at this index) 78 | 79 | # loop through data to determine all signals 80 | while end < 0: 81 | curr_window = plot_df[start:end] 82 | action = self.determine_signal(curr_window)[0] 83 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 84 | end += 1 85 | start += 1 86 | 87 | # compute final signal 88 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-100:])[0] 89 | 90 | def plot_graph(self): 91 | 92 | # deep copy of data so original is not impacted 93 | plot_df = self.df.copy(deep=True) 94 | 95 | # determine all signals for the dataset 96 | self.find_all_signals(plot_df) 97 | 98 | # initialise visualisation object for plotting 99 | visualisation = v.Visualise(plot_df) 100 | 101 | # determining one buy signal example for plotting 102 | visualisation.determine_buy_marker() 103 | 104 | # determining one sell signal example for plotting 105 | visualisation.determine_sell_marker() 106 | 107 | # add subplots of 200ema and awesome oscillator 108 | 109 | visualisation.add_subplot(plot_df['aroon_up'], panel=1, color="blue", ylabel="Aroon Up") 110 | visualisation.add_subplot(plot_df['aroon_down'], panel=1, color="pink", ylabel="Aroon Up") 111 | 112 | # create final plot with title 113 | visualisation.plot_graph("Aroon Indicator Strategy") 114 | -------------------------------------------------------------------------------- /trading_strategies/awesome_saucer.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import trading_strategies.visualise as v 4 | 5 | ''' 6 | The Awesome Oscillator Saucers strategy looks for a bullish or bearish saucer pattern in the Awesome Oscillator, where close price is greater than 200 EMA. 7 | A bullish saucer pattern consists of 3 positive AO bars which form the curve of a saucer (i.e. the middle value is smallest). 8 | A bearish saucer patter consists of 3 negative AO bars which form the curve of an upside down saucer (i.e. the middle value is greatest (least negative)). 9 | 10 | Author: Cheryl 11 | ''' 12 | 13 | class AwesomeOscillatorSaucer: 14 | def __init__(self, file_path): 15 | self.max_window = 300 # set to 300 for better understanding in plot (can see 200EMA through a 100 candlestick period). MIN value 200 16 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos"))[-self.max_window:] 17 | 18 | def calculate_awesome_oscillator(self): 19 | # initialise Awesome Oscillator 20 | awes_osc = ta.momentum.AwesomeOscillatorIndicator(high = self.df['high'], low = self.df['low']) 21 | # Calculate 22 | self.df['awesome_oscillator'] = awes_osc.ao() 23 | 24 | def calculate_ema(self): 25 | # initialise EMA indicator for 200 time periods 26 | indicator_ema = ta.trend.EMAIndicator(close = self.df['close'], window = 200) 27 | # Calculate 28 | self.df['200ema'] = indicator_ema.ema_indicator() 29 | 30 | def determine_signal(self, dframe): 31 | action = 0 32 | ema_dist = dframe['close'].iloc[-1] - dframe['200ema'].iloc[-1] 33 | 34 | bar_1 = dframe['awesome_oscillator'].iloc[-3] 35 | bar_2 = dframe['awesome_oscillator'].iloc[-2] 36 | bar_3 = dframe['awesome_oscillator'].iloc[-1] 37 | curr_close = dframe['close'].iloc[-1] 38 | curr_200ema = dframe['200ema'].iloc[-1] 39 | 40 | # BUY CRITERIA: CONSECUTIVELY: all 3 bars positive, 2 decreasing awesome oscillator values followed by an increase, and close is above the 200EMA 41 | if bar_1 > 0 and bar_2 > 0 and bar_3 > 0 and \ 42 | bar_1 > bar_2 and bar_2 < bar_3 and curr_close > curr_200ema: 43 | action = 1 44 | 45 | # SELL CRITERIA: CONSECUTIVELY: all 3 bars negative, 2 increasing awesome oscillator values followed by a decrease, and close is below the 200EMA 46 | elif bar_1 < 0 and bar_2 < 0 and bar_3 < 0 and\ 47 | bar_1 < bar_2 and bar_2 > bar_3 and curr_close < curr_200ema: 48 | action = -1 49 | 50 | return (action, ema_dist) 51 | 52 | def run_awesome_oscillator_saucer(self): 53 | self.calculate_awesome_oscillator() 54 | self.calculate_ema() 55 | signal = self.determine_signal(self.df) 56 | return signal, self.df 57 | 58 | ''' The following methods are for plotting ''' 59 | 60 | def find_all_signals(self, plot_df): 61 | # assign intitial value of hold 62 | plot_df['signal'] = 0 63 | 64 | start = -1*len(plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 65 | end = start + 200 # where the current window will stop (exclusive of the element at this index) 66 | 67 | # loop through data to determine all signals 68 | while end < 0: 69 | curr_window = plot_df[start:end] 70 | action = self.determine_signal(curr_window)[0] 71 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 72 | end += 1 73 | start += 1 74 | 75 | # compute final signal 76 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-200:])[0] 77 | 78 | def plot_graph(self): 79 | 80 | # deep copy of data so original is not impacted 81 | plot_df = self.df.copy(deep=True) 82 | 83 | # determine all signals for the dataset 84 | self.find_all_signals(plot_df) 85 | 86 | # initialise visualisation object for plotting 87 | visualisation = v.Visualise(plot_df) 88 | 89 | # determining one buy signal example for plotting 90 | visualisation.determine_buy_marker() 91 | 92 | # determining one sell signal example for plotting 93 | visualisation.determine_sell_marker() 94 | 95 | # add subplots of 200ema and awesome oscillator 96 | visualisation.add_subplot(plot_df['200ema'], color="orange") 97 | visualisation.add_subplot(plot_df['awesome_oscillator'], type="bar", panel = 1, ylabel="Awesome\nOscillator") 98 | 99 | # create final plot with title 100 | visualisation.plot_graph("Awesome Oscillator Saucers Strategy") -------------------------------------------------------------------------------- /trading_strategies/awesome_zero_crossover.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import trading_strategies.visualise as v 4 | 5 | ''' 6 | The Awesome Oscillator Zero Crossover strategy signals buying and selling opportunities when the Awesome Oscillator (AO) crosses to above or below 0. 7 | When the AO crosses above 0, we wait for 3 consecutive increasing values of AO to confirm bullish movement and then buy. 8 | When the AO crosses below 0, we wait for 3 consecutive decreasing values of AO to confirm bearish movement and then sell. 9 | 10 | Author: Cheryl 11 | ''' 12 | class AwesomeOscillatorZeroCrossover: 13 | def __init__(self, file_path): 14 | self.max_window = 134 # set to 134 for graphing purposes. Need minimum 37 15 | self.df = pd.read_csv(file_path)[-self.max_window:] 16 | self.high = self.df['high'] 17 | self.close = self.df['close'] 18 | self.low = self.df['low'] 19 | 20 | def calculate_awesome_oscillator(self): 21 | # initialise Awesome Oscillator 22 | awes_osc = ta.momentum.AwesomeOscillatorIndicator(high = self.high, low = self.low) 23 | # Calculate 24 | self.df['awesome_oscillator'] = awes_osc.ao() 25 | 26 | def determine_signal(self, dframe): 27 | action = 0 28 | 29 | # SELL CRITERIA: awesome oscillator crosses from above to below the zero line, followed by 3 decreasing values 30 | if dframe['awesome_oscillator'].iloc[-4] >= 0 and dframe['awesome_oscillator'].iloc[-3] <= 0 and \ 31 | dframe['awesome_oscillator'].iloc[-2] < dframe['awesome_oscillator'].iloc[-3] and \ 32 | dframe['awesome_oscillator'].iloc[-1] < dframe['awesome_oscillator'].iloc[-2]: 33 | action = -1 34 | 35 | # BUY CRITERIA: awesome oscillator crosses from below to above the zero line, followed by 3 increasing values 36 | elif dframe['awesome_oscillator'].iloc[-4] <= 0 and dframe['awesome_oscillator'].iloc[-3] >= 0 and \ 37 | dframe['awesome_oscillator'].iloc[-2] > dframe['awesome_oscillator'].iloc[-3] and \ 38 | dframe['awesome_oscillator'].iloc[-1] > dframe['awesome_oscillator'].iloc[-2]: 39 | action = 1 40 | 41 | return (action, dframe['awesome_oscillator'].iloc[-1]) 42 | 43 | def run_awesome_oscillator_zero_crossover(self): 44 | self.calculate_awesome_oscillator() 45 | signal = self.determine_signal(self.df) 46 | return signal, self.df 47 | 48 | ''' The following methods are for plotting ''' 49 | def find_all_signals(self, plot_df): 50 | # assign intitial value of hold 51 | plot_df['signal'] = 0 52 | 53 | start = -1*len(plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 54 | end = start + 37 # where the current window will stop (exclusive of the element at this index) 55 | 56 | # loop through data to determine all signals 57 | while end < 0: 58 | curr_window = plot_df[start:end] 59 | action = self.determine_signal(curr_window)[0] 60 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 61 | end += 1 62 | start += 1 63 | 64 | # compute final signal 65 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-37:])[0] 66 | 67 | def plot_graph(self): 68 | 69 | # deep copy of data so original is not impacted 70 | plot_df = self.df.copy(deep=True) 71 | 72 | # determine all signals for the dataset 73 | self.find_all_signals(plot_df) 74 | 75 | # initialise visualisation object for plotting 76 | visualisation = v.Visualise(plot_df) 77 | 78 | # determining one buy signal example for plotting 79 | visualisation.determine_buy_marker() 80 | 81 | # determining one sell signal example for plotting 82 | visualisation.determine_sell_marker() 83 | 84 | # add subplot of awesome oscillator 85 | visualisation.add_subplot(plot_df['awesome_oscillator'], type="bar", panel = 1, ylabel="Awesome\nOscillator") 86 | 87 | # create final plot with title 88 | visualisation.plot_graph("Awesome Oscillator Zero Line Crossover Strategy") -------------------------------------------------------------------------------- /trading_strategies/blade_runner.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | import mplfinance as mpf 5 | import ta 6 | import trading_strategies.visualise as v 7 | 8 | ''' 9 | ### Author: Wilson ### 10 | Strategy from: 11 | https://www.ig.com/au/trading-strategies/best-forex-trading-strategies-and-tips-190520#Bladerunner 12 | 13 | The first candlestick that touches the EMA is called the ‘signal candle’, 14 | The second candle that moves away from the EMA again is the ‘confirmatory candle’. 15 | Traders would place their open orders at this price level to take advantage of the rebounding price. 16 | 17 | ''' 18 | 19 | class BladeRunner: 20 | 21 | def __init__(self, file_path): 22 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos")) 23 | #self.df = pd.DataFrame(file_path) 24 | 25 | def add_ema(self): 26 | self.df['ema_20'] = self.df['close'].ewm(span = 20, min_periods = 20).mean() 27 | 28 | # determine and signal for particular index 29 | def determine_signal(self, dframe): 30 | 31 | signal = 0 32 | # BUY if first candle stick touches ema and then next candle stick rebounds off it 33 | if((dframe['low'].iloc[-2] <= dframe['ema_20'].iloc[-2] and dframe['ema_20'].iloc[-2] <= dframe['high'].iloc[-2]) & (dframe['close'].iloc[-1] > dframe['close'].iloc[-2])): 34 | signal = 1 35 | 36 | # SELL if first candle stick touches ema and then next candle stick rebounds off it 37 | if((dframe['low'].iloc[-2] <= dframe['ema_20'].iloc[-2] and dframe['ema_20'].iloc[-2] <= dframe['high'].iloc[-2]) & (dframe['close'].iloc[-1] < dframe['close'].iloc[-2])): 38 | signal = -1 39 | 40 | return signal 41 | 42 | # determine and return additional useful information for last data point in given df 43 | def determine_additional_info(self, dframe): 44 | 45 | # strength of rebound 46 | return dframe.iloc[-1]['close'] - dframe.iloc[-2]['close'] 47 | 48 | # run strategy 49 | def run_blade_runner(self): 50 | 51 | #perform calculations 52 | self.add_ema() 53 | 54 | #generate data for return tuple at index -1 in the dataset aka most recent data point 55 | signal = self.determine_signal(self.df) 56 | additional_info = self.determine_additional_info(self.df) 57 | 58 | #create return tuple and append data 59 | result = [] 60 | result.append(signal) 61 | result.append(additional_info) 62 | 63 | return tuple(result), self.df 64 | 65 | def find_all_signals(self, plot_df): 66 | # assign intitial value of hold 67 | plot_df['signal'] = 0 68 | 69 | start = -1*len(plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 70 | end = start + 200 # where the current window will stop (exclusive of the element at this index) 71 | 72 | # loop through data to determine all signals 73 | while end < 0: 74 | curr_window = plot_df[start:end] 75 | action = self.determine_signal(curr_window) 76 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 77 | end += 1 78 | start += 1 79 | 80 | # compute final signal 81 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-200:]) 82 | 83 | def plot_graph(self): 84 | 85 | # deep copy of data so original is not impacted 86 | plot_df = self.df.copy(deep=True) 87 | 88 | # determine all signals for the dataset 89 | self.find_all_signals(plot_df) 90 | 91 | # initialise visualisation object for plotting 92 | visualisation = v.Visualise(plot_df) 93 | 94 | # determining one buy signal example for plotting 95 | visualisation.determine_buy_marker() 96 | 97 | # determining one sell signal example for plotting 98 | visualisation.determine_sell_marker() 99 | 100 | # add subplots of 20 period ema 101 | visualisation.add_subplot(plot_df['ema_20'], color="orange") 102 | #visualisation.add_subplot(plot_df['awesome_oscillato'], type="bar", panel = 1, ylabel="Blade\nRunner") 103 | 104 | # create final plot with title 105 | visualisation.plot_graph("Blade Runner Strategy") 106 | 107 | #strategy = BladeRunner(r"C:\Users\Wilson\Documents\INFO3600\AUD_USD_H4.csv") 108 | #print(strategy.run_blade_runner()) 109 | #strategy.plot_graph() 110 | -------------------------------------------------------------------------------- /trading_strategies/bollingerbands_rsi_2.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | import mplfinance as mpf 5 | import ta 6 | import trading_strategies.visualise as v 7 | 8 | ''' 9 | ### Author: Wilson ### 10 | Strategy from: 11 | 12 | https://www.theancientbabylonians.com/the-bollinger-bands-and-relative-strength-index-rsi-strategy/ 13 | 14 | This strategy identifies over bought and over sold conditions and back checks this against the bollinger band 15 | to ensure robustness of signals. 16 | 17 | ''' 18 | 19 | class BollingerBandsAndRSI2: 20 | 21 | #constructor 22 | def __init__(self, file_path): 23 | #self.df = pd.DataFrame(file_path) 24 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos")) 25 | 26 | # add bollinger bands (bb) to indicate volatilty 27 | def add_bollinger_bands(self): 28 | self.df['bb_high_band'] = ta.volatility.bollinger_hband(self.df['close'], window = 14, window_dev= 2, fillna = False) 29 | self.df['bb_low_band'] = ta.volatility.bollinger_lband(self.df['close'], window = 14, window_dev = 2, fillna = False) 30 | 31 | # add rsi to identify overbought/oversold conditions 32 | def add_rsi(self): 33 | self.df['rsi'] = ta.momentum.rsi(self.df['close'], window = 14, fillna = False) 34 | 35 | # determine and signal for particular index 36 | def determine_signal(self, dframe): 37 | 38 | signal = 0 39 | 40 | #BUY if price is below low bollinger band and rsi is less than 30 41 | if((dframe['close'].iloc[-1] <= dframe['bb_low_band'].iloc[-1]) & (dframe['rsi'].iloc[-1] <= 30)): 42 | signal = 1 43 | 44 | #SELL if price is above high bollinger band as rsi is greater than 70 45 | if((dframe['close'].iloc[-1] >= dframe['bb_high_band'].iloc[-1]) & (dframe['rsi'].iloc[-1] >= 70)): 46 | signal = -1 47 | 48 | return signal 49 | 50 | # determine and return additional useful information 51 | def determine_additional_info(self, dframe): 52 | 53 | return dframe.iloc[-1]['bb_high_band'] - dframe.iloc[-1]['bb_low_band'] 54 | 55 | # run the strategy 56 | def run_bollingerbands_rsi_2(self): 57 | 58 | # perform calculations 59 | self.add_bollinger_bands() 60 | self.add_rsi() 61 | 62 | # generate data for return tuple 63 | signal = self.determine_signal(self.df) 64 | additional_info = self.determine_additional_info(self.df) 65 | 66 | #create return tuple and append data 67 | result = [] 68 | result.append(signal) 69 | result.append(additional_info) 70 | 71 | return tuple(result), self.df 72 | 73 | ''' The following methods are for plotting ''' 74 | 75 | def find_all_signals(self, plot_df): 76 | # assign intitial value of hold 77 | plot_df['signal'] = 0 78 | 79 | start = -1*len(plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 80 | end = start + 200 # where the current window will stop (exclusive of the element at this index) 81 | 82 | # loop through data to determine all signals 83 | while end < 0: 84 | curr_window = plot_df[start:end] 85 | action = self.determine_signal(curr_window) 86 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 87 | end += 1 88 | start += 1 89 | 90 | # compute final signal 91 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-200:]) 92 | 93 | def plot_graph(self): 94 | 95 | # deep copy of data so original is not impacted 96 | plot_df = self.df.copy(deep=True) 97 | 98 | # determine all signals for the dataset 99 | self.find_all_signals(plot_df) 100 | 101 | # initialise visualisation object for plotting 102 | visualisation = v.Visualise(plot_df) 103 | 104 | # determining one buy signal example for plotting 105 | visualisation.determine_buy_marker() 106 | 107 | # determining one sell signal example for plotting 108 | visualisation.determine_sell_marker() 109 | 110 | # add subplots of bb and rsi 111 | visualisation.add_subplot(plot_df['bb_high_band'], color="orange") 112 | visualisation.add_subplot(plot_df['bb_low_band'], color="orange") 113 | visualisation.add_subplot(plot_df['rsi'], type="line", panel = 1, ylabel="RSI") 114 | 115 | # create final plot with title 116 | visualisation.plot_graph("Bollinger Bands RSI 2 Strategy") 117 | 118 | #strategy = BollingerBandsAndRSI2(r"C:\Users\Wilson\Documents\INFO3600\info3600_group1-project\back_testing\3yr_historical_data\3YR_AUD_USD\FXCM_AUD_USD_H4.csv") 119 | #strategy.run_bollingerbands_rsi_2() 120 | #strategy.plot_graph() 121 | -------------------------------------------------------------------------------- /trading_strategies/cci_moving_average.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 4 | @author: mingyu and caitlin 5 | 6 | This strategy combines the CCI, the commodity channel index with a simple moving average for 100 periods. 7 | The CCI is a trend indicator that shows oversold and overbought conditions. Combine with the sma100 8 | to attempt to filter some false signals. 9 | """ 10 | 11 | import datetime 12 | import pandas as pd 13 | import ta 14 | import trading_strategies.visualise as v 15 | 16 | 17 | class CciMovingAverage: 18 | 19 | # loading the data in from file_path 20 | def __init__(self, file_path): 21 | self.df = pd.read_csv(file_path) 22 | self.high = self.df['high'] 23 | self.low = self.df['low'] 24 | self.close = self.df['close'] 25 | 26 | # calculates the cci 27 | def calculate_cci(self): 28 | self.df['cci'] = ta.trend.CCIIndicator(high=self.high, low=self.low, close=self.close).cci() 29 | 30 | # calculates the period 100 moving average 31 | def calculate_moving_average(self): 32 | self.df['average'] = (self.df['high'] + self.df['low'] + self.df['close']) / 3 33 | self.df['period_100_average'] = self.df['average'].rolling(window=100).mean() 34 | 35 | 36 | # Runs the commodity channel index with moving average strategy 37 | def determine_signal(self, dframe): 38 | 39 | # initialise all signals to hold: 0 40 | signal = 0 41 | 42 | # A buy entry signal is when cci left oversold zone, i.e. just above -100, and price intersects the period 100 moving average from below 43 | if dframe['cci'].iloc[-1] > -100 and dframe['cci'].iloc[-2] <= -100 and dframe['close'].iloc[-1] > dframe['period_100_average'].iloc[-1] and dframe['close'].iloc[-2] <= dframe['period_100_average'].iloc[-2]: 44 | signal = 1 45 | # A sell entry signal is when cci left overbought zone, i.e. just below 100, and price intersects the period 100 moving average from above 46 | elif dframe['cci'].iloc[-1] < 100 and dframe['cci'].iloc[-2] >= 100 and dframe['close'].iloc[-1] < dframe['period_100_average'].iloc[-1] and dframe['close'].iloc[-2] >= dframe['period_100_average'].iloc[-2]: 47 | signal = -1 48 | 49 | return (signal, dframe['period_100_average'].iloc[-1]) 50 | 51 | def run(self): 52 | self.calculate_cci() 53 | self.calculate_moving_average() 54 | signal = self.determine_signal(self.df) 55 | return signal, self.df 56 | 57 | ''' 58 | the following methods are for plotting 59 | ''' 60 | 61 | def find_all_signals(self, plot_df): 62 | # assign intitial value of hold 63 | plot_df['signal'] = 0 64 | 65 | start = -1 * len( 66 | plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 67 | end = start + 101 # where the current window will stop (exclusive of the element at this index) 68 | 69 | # loop through data to determine all signals 70 | while end < 0: 71 | curr_window = plot_df[start:end] 72 | action = self.determine_signal(curr_window)[0] 73 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 74 | additional_info = self.determine_signal(curr_window)[1] 75 | plot_df.loc[plot_df.index[end - 1], 'additional_info'] = additional_info 76 | end += 1 77 | start += 1 78 | 79 | # compute final signal 80 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-101:])[0] 81 | 82 | def plot_graph(self): 83 | 84 | # deep copy of data so original is not impacted 85 | plot_df = self.df.copy(deep=True) 86 | 87 | # determine all signals for the dataset 88 | self.find_all_signals(plot_df) 89 | 90 | plot_df['zero_line'] = 0 91 | plot_df['100_line'] = 100 92 | plot_df['-100_line'] = -100 93 | 94 | # initialise visualisation object for plotting 95 | visualisation = v.Visualise(plot_df) 96 | 97 | # determining one buy signal example for plotting 98 | visualisation.determine_buy_marker() 99 | 100 | # determining one sell signal example for plotting 101 | visualisation.determine_sell_marker() 102 | 103 | # add subplots of senkou span A and B to form the ichimoku cloud, and the parabolic sar dots 104 | visualisation.add_subplot(plot_df['period_100_average'], color='red', width=1, ylabel='period100ma') 105 | visualisation.add_subplot(plot_df['cci'], panel=1, color='b', width=0.75, ylabel='CCI') 106 | visualisation.add_subplot(plot_df['100_line'], panel=1, color='k', secondary_y=False, width=0.75, 107 | linestyle='solid') 108 | visualisation.add_subplot(plot_df['-100_line'], panel=1, color='k', secondary_y=False, width=0.75, 109 | linestyle='solid') 110 | 111 | # create final plot with title 112 | visualisation.plot_graph("CCI MA Strategy") -------------------------------------------------------------------------------- /trading_strategies/commodity_channel_index.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Sep 16 12:58:56 2020 4 | 5 | @author: mingyu and caitlin 6 | 7 | This strategy uses the CCI (Commodity Channel Index) to determine buy/sell signals. The CCI is a momentum based oscillator used for determining overbought 8 | and oversold conditions. 9 | """ 10 | 11 | import pandas as pd 12 | import ta 13 | import trading_strategies.visualise as v 14 | 15 | 16 | class CommodityChannelIndex: 17 | 18 | # loading the data in from file_path 19 | def __init__(self, file_path): 20 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos")) 21 | self.high = self.df['high'] 22 | self.low = self.df['low'] 23 | self.close = self.df['close'] 24 | 25 | # calculates the cci 26 | def calculate_cci(self): 27 | self.df['cci'] = ta.trend.CCIIndicator(high=self.high, low=self.low, close=self.close).cci() 28 | 29 | # determine when to output buy/sell or hold signal 30 | def determine_signal(self, dframe): 31 | 32 | # initialise all signals to hold: 0 33 | signal = 0 34 | 35 | # A buy entry signal is given when the cci leaves the oversold zone, i.e. just above -100 36 | if dframe['cci'].iloc[-1] > -100 and dframe['cci'].iloc[-2] <= -100: 37 | signal = 1 38 | # A sell entry signal is given when the cci leaves the overbought zone, i.e. just above 100 39 | elif dframe['cci'].iloc[-1] < 100 and dframe['cci'].iloc[-2] >= 100: 40 | signal = -1 41 | 42 | return (signal, dframe['close'].iloc[-1]) 43 | 44 | # Runs the commodity channel index strategy 45 | def run(self): 46 | self.calculate_cci() 47 | signal = self.determine_signal(self.df) 48 | return signal, self.df 49 | 50 | ''' The following methods are for plotting ''' 51 | 52 | 53 | def find_all_signals(self, plot_df): 54 | # assign intitial value of hold 55 | plot_df['signal'] = 0 56 | 57 | start = -1 * len( 58 | plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 59 | end = start + 21 # where the current window will stop (exclusive of the element at this index) 60 | 61 | # loop through data to determine all signals 62 | while end < 0: 63 | curr_window = plot_df[start:end] 64 | action = self.determine_signal(curr_window)[0] 65 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 66 | additional_info = self.determine_signal(curr_window)[1] 67 | plot_df.loc[plot_df.index[end - 1], 'additional_info'] = additional_info 68 | end += 1 69 | start += 1 70 | 71 | # compute final signal 72 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-21:])[0] 73 | 74 | def plot_graph(self): 75 | 76 | # deep copy of data so original is not impacted 77 | plot_df = self.df.copy(deep=True) 78 | 79 | # determine all signals for the dataset 80 | self.find_all_signals(plot_df) 81 | 82 | plot_df['zero_line'] = 0 83 | plot_df['100_line'] = 100 84 | plot_df['-100_line'] = -100 85 | 86 | # initialise visualisation object for plotting 87 | visualisation = v.Visualise(plot_df) 88 | 89 | # determining one buy signal example for plotting 90 | visualisation.determine_buy_marker() 91 | 92 | # determining one sell signal example for plotting 93 | visualisation.determine_sell_marker() 94 | 95 | # add subplots of senkou span A and B to form the ichimoku cloud, and the parabolic sar dots 96 | visualisation.add_subplot(plot_df['cci'], panel=1, color='m', width=0.75, ylabel='CCI') 97 | visualisation.add_subplot(plot_df['100_line'], panel=1, color='k', secondary_y=False, width=0.75, linestyle='solid') 98 | visualisation.add_subplot(plot_df['-100_line'], panel=1, color='k', secondary_y=False, width=0.75, linestyle='solid') 99 | 100 | # create final plot with title 101 | visualisation.plot_graph("Commodity Channel Index Strategy") 102 | -------------------------------------------------------------------------------- /trading_strategies/donchian_atr.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | import mplfinance as mpf 5 | import ta 6 | import trading_strategies.visualise as v 7 | 8 | ''' 9 | ### Author: Wilson ### 10 | Strategy from: 11 | https://www.tradingwithrayner.com/donchian-channel-indicator/#atr 12 | ''' 13 | 14 | class DonchianATR(): 15 | 16 | def __init__(self, file_path): 17 | self.df = pd.read_csv(file_path) 18 | #self.df = pd.DataFrame(file_path) 19 | self.high = self.df['high'] 20 | self.low = self.df['low'] 21 | self.close = self.df['close'] 22 | 23 | def add_atr(self): 24 | self.df['atr'] = ta.volatility.AverageTrueRange(high = self.high, low = self.low, close = self.close, n = 14).average_true_range() 25 | 26 | def add_atr_two_year_low(self): 27 | self.df['atr_two_year_low'] = self.df['atr'].shift(1).rolling(window = 48).min() 28 | 29 | def add_donchian_high_band(self): 30 | self.df['donchian_high_band'] = ta.volatility.DonchianChannel(high = self.high, low = self.low, close = self.close, n = 20).donchian_channel_hband() 31 | 32 | def add_donchian_low_band(self): 33 | self.df['donchian_low_band'] = ta.volatility.DonchianChannel(high = self.high, low = self.low, close = self.close, n = 20).donchian_channel_lband() 34 | 35 | # determine and signal for particular index 36 | def determine_signal(self, dframe): 37 | 38 | signal = 0 39 | 40 | # BUY if ATR is below 2 year low and price breaks above donchian high band 41 | if((dframe['atr'].iloc[-1] < dframe['atr_two_year_low'].iloc[-1]) & (dframe['high'].iloc[-1] >= dframe['donchian_high_band'].iloc[-1])): 42 | signal = 1 43 | 44 | # SELL if ATR is below 2 year low and price drops below donchian low band 45 | if((dframe['atr'].iloc[-1] < dframe['atr_two_year_low'].iloc[-1]) & (dframe['low'].iloc[-1] <= dframe['donchian_low_band'].iloc[-1])): 46 | signal = -1 47 | 48 | return signal 49 | 50 | # determine and return additional useful information 51 | def determine_additional_info(self, dframe): 52 | 53 | return dframe.iloc[-1]['donchian_high_band'] - dframe.iloc[-1]['atr_two_year_low'] 54 | 55 | def run_donchian_atr(self): 56 | 57 | # perform calculations 58 | self.add_atr() 59 | self.add_atr_two_year_low() 60 | self.add_donchian_high_band() 61 | self.add_donchian_low_band() 62 | 63 | # generate data for return tuple 64 | signal = self.determine_signal(self.df) 65 | additional_info = self.determine_additional_info(self.df) 66 | 67 | # create return tuple and append data 68 | result = [] 69 | result.append(signal) 70 | result.append(additional_info) 71 | 72 | return tuple(result), self.df 73 | 74 | def find_all_signals(self, plot_df): 75 | # assign intitial value of hold 76 | plot_df['signal'] = 0 77 | 78 | start = -1*len(plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 79 | end = start + 200 # where the current window will stop (exclusive of the element at this index) 80 | 81 | # loop through data to determine all signals 82 | while end < 0: 83 | curr_window = plot_df[start:end] 84 | action = self.determine_signal(curr_window) 85 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 86 | end += 1 87 | start += 1 88 | 89 | # compute final signal 90 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-200:]) 91 | 92 | def plot_graph(self): 93 | 94 | # deep copy of data so original is not impacted 95 | plot_df = self.df.copy(deep=True) 96 | 97 | # determine all signals for the dataset 98 | self.find_all_signals(plot_df) 99 | 100 | # initialise visualisation object for plotting 101 | visualisation = v.Visualise(plot_df) 102 | 103 | # determining one buy signal example for plotting 104 | visualisation.determine_buy_marker() 105 | 106 | # determining one sell signal example for plotting 107 | visualisation.determine_sell_marker() 108 | 109 | # add subplots of donchian channels 110 | visualisation.add_subplot(plot_df['donchian_high_band'], color="orange") 111 | visualisation.add_subplot(plot_df['donchian_low_band'], color="orange") 112 | 113 | visualisation.add_subplot(plot_df['atr'], type="line", panel = 1, ylabel="ATR and 2 year low") 114 | visualisation.add_subplot(plot_df['atr_two_year_low'], type="line", panel = 1) 115 | 116 | # create final plot with title 117 | visualisation.plot_graph("Donchian ATR Strategy") 118 | -------------------------------------------------------------------------------- /trading_strategies/donchian_breakout.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import trading_strategies.visualise as v 4 | 5 | ''' 6 | ### Author: Wilson ### 7 | Strategy from: 8 | https://www.ig.com/au/trading-strategies/a-trader-s-guide-to-donchian-channels-200218 9 | 10 | This strategy attempts to identify breakouts from current trends 11 | 12 | ''' 13 | 14 | class DonchianBreakout: 15 | 16 | def __init__(self, file_path): 17 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos")) 18 | #self.df = pd.DataFrame(file_path) 19 | self.high = self.df['high'] 20 | self.low = self.df['low'] 21 | self.close = self.df['close'] 22 | 23 | def add_donchian_high_band(self): 24 | self.df['donchian_high_band'] = ta.volatility.DonchianChannel(high = self.high, low = self.low, close = self.close, window = 20).donchian_channel_hband() 25 | 26 | def add_donchian_low_band(self): 27 | self.df['donchian_low_band'] = ta.volatility.DonchianChannel(high = self.high, low = self.low, close = self.close, window = 20).donchian_channel_lband() 28 | 29 | # determine and signal for particular index 30 | def determine_signal(self, dframe): 31 | 32 | signal = 0 33 | 34 | # BUY if price exceeds donchian high band 35 | if(dframe['close'].iloc[-1] >= dframe['donchian_high_band'].iloc[-1]): 36 | signal = 1 37 | 38 | # SELL if price drosp below donchian low band 39 | if(dframe['close'].iloc[-1] <= dframe['donchian_low_band'].iloc[-1]): 40 | signal = -1 41 | 42 | return signal 43 | 44 | # determine and return additional useful information 45 | def determine_additional_info(self, dframe): 46 | 47 | return dframe.iloc[-1]['donchian_high_band'] - dframe.iloc[-1]['donchian_low_band'] 48 | 49 | def run_donchian_breakout(self): 50 | 51 | # perform calculations 52 | self.add_donchian_high_band() 53 | self.add_donchian_low_band() 54 | 55 | # generate data for return tuple 56 | signal = self.determine_signal(self.df) 57 | additional_info = self.determine_additional_info(self.df) 58 | 59 | # create return tuple and append data 60 | result = [] 61 | result.append(signal) 62 | result.append(additional_info) 63 | 64 | return tuple(result), self.df 65 | 66 | def find_all_signals(self, plot_df): 67 | # assign intitial value of hold 68 | plot_df['signal'] = 0 69 | 70 | start = -1*len(plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 71 | end = start + 200 # where the current window will stop (exclusive of the element at this index) 72 | 73 | # loop through data to determine all signals 74 | while end < 0: 75 | curr_window = plot_df[start:end] 76 | action = self.determine_signal(curr_window) 77 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 78 | end += 1 79 | start += 1 80 | 81 | # compute final signal 82 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-200:]) 83 | 84 | def plot_graph(self): 85 | 86 | # deep copy of data so original is not impacted 87 | plot_df = self.df.copy(deep=True) 88 | 89 | # determine all signals for the dataset 90 | self.find_all_signals(plot_df) 91 | 92 | # initialise visualisation object for plotting 93 | visualisation = v.Visualise(plot_df) 94 | 95 | # determining one buy signal example for plotting 96 | visualisation.determine_buy_marker() 97 | 98 | # determining one sell signal example for plotting 99 | visualisation.determine_sell_marker() 100 | 101 | # add subplots of donchian channels 102 | visualisation.add_subplot(plot_df['donchian_high_band'], color="orange") 103 | visualisation.add_subplot(plot_df['donchian_low_band'], color="orange") 104 | 105 | # create final plot with title 106 | visualisation.plot_graph("Donchian Breakout Strategy") 107 | -------------------------------------------------------------------------------- /trading_strategies/donchian_middle.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: caitlin 4 | This strategy uses Donchian Channels. It examines whether the close is higher or lower than the middle donchian line. 5 | """ 6 | 7 | import pandas as pd 8 | import trading_strategies.visualise as v 9 | import ta 10 | 11 | class DonchianMiddle: 12 | # loading the data in from file_path 13 | def __init__(self, file_path): 14 | self.max_window = 1001 # uncomment for graphing purposes 15 | self.df = pd.read_csv(file_path)[-self.max_window:] 16 | self.high = self.df['high'] 17 | self.close = self.df['close'] 18 | self.low = self.df['low'] 19 | 20 | 21 | def calculate_donchian_middle(self): 22 | donch_mid_ind = ta.volatility.DonchianChannel(high=self.high, low=self.low, close=self.close, n=20) 23 | self.df['donch_mid'] = donch_mid_ind.donchian_channel_mband() 24 | 25 | 26 | def determine_signal(self, dframe): 27 | dframe['signal'] = 0 28 | # A buy entry signal is given when close exceeds the middle donchian line 29 | dframe.loc[(dframe['close'] > dframe['donch_mid']), 'signal'] = 1 30 | 31 | # A sell entry signal is given when close goes below the middle donchian line 32 | dframe.loc[(dframe['close'] < dframe['donch_mid']), 'signal'] = -1 33 | 34 | 35 | signal_col = dframe.columns.get_loc('signal') 36 | return (dframe.iloc[-1, signal_col], dframe['donch_mid'].iloc[-1]) 37 | 38 | # Runs the indicator 39 | def run_donchian_middle(self): 40 | self.calculate_donchian_middle() 41 | signal = self.determine_signal(self.df) 42 | return signal, self.df 43 | 44 | ''' 45 | the following methods are for plotting 46 | ''' 47 | 48 | def find_all_signals(self, plot_df): 49 | # assign intitial value of hold 50 | plot_df['signal'] = 0 51 | 52 | start = -1 * len( 53 | plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 54 | end = start + 100 # where the current window will stop (exclusive of the element at this index) 55 | 56 | # loop through data to determine all signals 57 | while end < 0: 58 | curr_window = plot_df[start:end] 59 | action = self.determine_signal(curr_window)[0] 60 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 61 | end += 1 62 | start += 1 63 | 64 | # compute final signal 65 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-100:])[0] 66 | 67 | def plot_graph(self): 68 | # deep copy of data so original is not impacted 69 | plot_df = self.df.copy(deep=True) 70 | 71 | # determine all signals for the dataset 72 | self.find_all_signals(plot_df) 73 | 74 | # initialise visualisation object for plotting 75 | visualisation = v.Visualise(plot_df) 76 | 77 | # determining one buy signal example for plotting 78 | visualisation.determine_buy_marker() 79 | 80 | # determining one sell signal example for plotting 81 | visualisation.determine_sell_marker() 82 | 83 | # add subplot of donchian middle band 84 | visualisation.add_subplot(plot_df['donch_mid'], color="blue", ylabel="Donchian\nMiddle Band") 85 | 86 | # create final plot with title 87 | visualisation.plot_graph("Donchian Middle Band Trading Strategy") 88 | -------------------------------------------------------------------------------- /trading_strategies/dpo_candlestick.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import trading_strategies.visualise as v 4 | 5 | ''' 6 | The DPO is an indicator designed to remove trend from price and make it easier to identify underlying cycles in price movement. 7 | When the DPO moves above zero, this indicates a potential bullish trend. On the other hand, when DPO moves below zero, this suggests a bearish sign. 8 | The criteria to buy is when DPO crosses above zero and the candle stick pattern suggests a bullish trend. 9 | The criteria to sell is when DPO crosses below zero and the candle stick pattern suggests a bearish trend. 10 | 11 | Author: Cheryl 12 | ''' 13 | 14 | class DpoCandlestick: 15 | def __init__(self, file_path): 16 | self.max_window = 100 # set to 100 for better understanding of trend in graph. MIN value 21 17 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos"))[-self.max_window:] 18 | 19 | def calculate_dpo(self): 20 | # initialise DPO indicator for n = 20 21 | indicator_dpo = ta.trend.DPOIndicator(close = self.df['close'], window = 20) 22 | # calculate values 23 | self.df['dpo'] = indicator_dpo.dpo() 24 | 25 | def determine_signal(self, dframe): 26 | action = 0 27 | candle_length = dframe['close'].iloc[-1] - dframe['open'].iloc[-1] 28 | 29 | curr_close = dframe['close'].iloc[-1] 30 | prev_close = dframe['close'].iloc[-2] 31 | 32 | curr_open = dframe['open'].iloc[-1] 33 | prev_open = dframe['open'].iloc[-2] 34 | 35 | # BUY CRITERIA: DPO crosses above 0, and bullish candle stick pattern 36 | if dframe['dpo'].iloc[-2] <= 0 and dframe['dpo'].iloc[-1] > 0: 37 | # Bullish Candlestick pattern 1: piercing line 38 | if curr_close > curr_open and curr_close > (prev_close + prev_open)/2 and prev_close < prev_open: 39 | action = 1 40 | # Bullish Candlestick Pattern 2: Bullish engulfing 41 | elif prev_close < prev_open and curr_close > curr_open and curr_close > prev_open and curr_open <= prev_close: 42 | action = 1 43 | 44 | # SELL CRITERIA: DPO crosses below 0, and bearish candles tick pattern 45 | if dframe['dpo'].iloc[-2] >= 0 and dframe['dpo'].iloc[-1] < 0: 46 | # Bearish Candlestick pattern 1: Dark Cloud Cover 47 | if prev_close > prev_open and curr_open > curr_close and curr_open > prev_close and curr_close < (prev_close + prev_open)/2: 48 | action = -1 49 | # Bearish Candlestick pattern 2: Bearish engulfing 50 | elif prev_close > prev_open and curr_open > curr_close and curr_close < prev_open and (curr_open - curr_close) > (prev_close - prev_open): 51 | action = -1 52 | 53 | return (action, candle_length) 54 | 55 | def run_dpo_candlestick(self): 56 | self.calculate_dpo() 57 | signal = self.determine_signal(self.df) 58 | return signal, self.df 59 | 60 | ''' The following methods are for plotting ''' 61 | 62 | def find_all_signals(self, plot_df): 63 | # assign intitial value of hold 64 | plot_df['signal'] = 0 65 | 66 | start = -1*len(plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 67 | end = start + 21 # where the current window will stop (exclusive of the element at this index) 68 | 69 | # loop through data to determine all signals 70 | while end < 0: 71 | curr_window = plot_df[start:end] 72 | action = self.determine_signal(curr_window)[0] 73 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 74 | end += 1 75 | start += 1 76 | 77 | # compute final signal 78 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-21:])[0] 79 | 80 | def plot_graph(self): 81 | 82 | # deep copy of data so original is not impacted 83 | plot_df = self.df.copy(deep=True) 84 | 85 | # add zero line series to show horizontal axis at dpo value of 0 86 | plot_df['zero_line'] = 0 87 | 88 | # determine all signals for the dataset 89 | self.find_all_signals(plot_df) 90 | 91 | # initialise visualisation object for plotting 92 | visualisation = v.Visualise(plot_df) 93 | 94 | # determining one buy signal example for plotting 95 | visualisation.determine_buy_marker() 96 | 97 | # determining one sell signal example for plotting 98 | visualisation.determine_sell_marker() 99 | 100 | # add subplots of zero line and dpo 101 | visualisation.add_subplot(plot_df['zero_line'], color="grey", panel=1, secondary_y=False) 102 | visualisation.add_subplot(plot_df['dpo'], color="royalblue", panel = 1, ylabel="DPO") 103 | 104 | # create final plot with title 105 | visualisation.plot_graph("DPO and Candlestick Pattern Strategy") -------------------------------------------------------------------------------- /trading_strategies/elder_ray.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import trading_strategies.visualise as v 4 | 5 | ''' 6 | The Elder Ray Index measures the amount of buying and selling power in a market with two indicators: Bull Power and Bear Power. 7 | This index is used in conjunction with a 21 EMA to further gauge the trend. 8 | We buy when Bear Power is negative but increasing, Bull Power is increasing and EMA is positively sloped. 9 | We sell when Bull Power is positive but decreasing, Bear Power is decreasing and EMA is negatively sloped. 10 | 11 | Author: Cheryl 12 | ''' 13 | class ElderRay: 14 | def __init__(self, file_path): 15 | self.max_window = 100 # set to 100 for better understanding of trend in graph. MIN value 22 16 | self.df = pd.read_csv(file_path)[-self.max_window:] 17 | 18 | def calculate_ema(self): 19 | # initialise EMA indicator for 21 time periods 20 | indicator_ema = ta.trend.EMAIndicator(close = self.df['close'], n = 21) 21 | # Calculate 22 | self.df['21_ema'] = indicator_ema.ema_indicator() 23 | 24 | def calculate_bull_power(self): 25 | self.df['bull_power'] = self.df['high'] - self.df['21_ema'] 26 | 27 | def calculate_bear_power(self): 28 | self.df['bear_power'] = self.df['low'] - self.df['21_ema'] 29 | 30 | def determine_signal(self, dframe): 31 | action = 0 32 | ema_dist = dframe['close'].iloc[-1] - dframe['21_ema'].iloc[-1] 33 | 34 | # BUY CRITERIA: Bear power’s value is negative but increasing, Bull power’s value is increasing and 21 EMA is increasing. 35 | if dframe['bear_power'].iloc[-1] < 0 and dframe['bear_power'].iloc[-1] > dframe['bear_power'].iloc[-2] \ 36 | and dframe['bull_power'].iloc[-1] > dframe['bull_power'].iloc[-2] and dframe['21_ema'].iloc[-1] > dframe['21_ema'].iloc[-2] : 37 | action = 1 38 | 39 | # SELL CRITERIA: Bull power’s value is positive but decreasing, Bear power’s value is decreasing and 21 EMA is decreasing. 40 | elif dframe['bull_power'].iloc[-1] > 0 and dframe['bull_power'].iloc[-1] < dframe['bull_power'].iloc[-2] \ 41 | and dframe['bear_power'].iloc[-1] < dframe['bear_power'].iloc[-2] and dframe['21_ema'].iloc[-1] < dframe['21_ema'].iloc[-2] : 42 | action = -1 43 | 44 | return (action, ema_dist) 45 | 46 | def run_elder_ray(self): 47 | self.calculate_ema() 48 | self.calculate_bull_power() 49 | self.calculate_bear_power() 50 | signal = self.determine_signal(self.df) 51 | return signal, self.df 52 | 53 | ''' The following methods are for plotting ''' 54 | 55 | def find_all_signals(self, plot_df): 56 | # assign intitial value of hold 57 | plot_df['signal'] = 0 58 | 59 | start = -1*len(plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 60 | end = start + 22 # where the current window will stop (exclusive of the element at this index) 61 | 62 | # loop through data to determine all signals 63 | while end < 0: 64 | curr_window = plot_df[start:end] 65 | action = self.determine_signal(curr_window)[0] 66 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 67 | end += 1 68 | start += 1 69 | 70 | # compute final signal 71 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-22:])[0] 72 | 73 | 74 | def plot_graph(self): 75 | 76 | # deep copy of data so original is not impacted 77 | plot_df = self.df.copy(deep=True) 78 | 79 | # determine all signals for the dataset 80 | self.find_all_signals(plot_df) 81 | 82 | # zero line series for horizontal axis at value 0 in bull power and bear power 83 | plot_df['zero_line'] = 0 84 | 85 | # initialise visualisation object for plotting 86 | visualisation = v.Visualise(plot_df) 87 | 88 | # determining one buy signal example for plotting 89 | visualisation.determine_buy_marker() 90 | 91 | # determining one sell signal example for plotting 92 | visualisation.determine_sell_marker() 93 | 94 | # add subplots of 21 ema, bull power, bear power, and zero lines 95 | visualisation.add_subplot(plot_df['21_ema'], color="royalblue") 96 | visualisation.add_subplot(plot_df['bull_power'], panel=1, type="bar", ylabel="Bull Power") 97 | visualisation.add_subplot(plot_df['bear_power'], panel=2, type="bar", ylabel="Bear Power") 98 | visualisation.add_subplot(plot_df['zero_line'], panel=1, secondary_y=False, color="gray") 99 | visualisation.add_subplot(plot_df['zero_line'], panel=2, secondary_y=False, color="gray") 100 | 101 | # create final plot with title 102 | visualisation.plot_graph("Elder Ray and 21 EMA Strategy") -------------------------------------------------------------------------------- /trading_strategies/elder_ray_alternative.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import trading_strategies.visualise as v 4 | 5 | ''' 6 | The Elder Ray Index measures the amount of buying and selling power in a market with two indicators: Bull Power and Bear Power. 7 | This index is used in conjunction with a 13 EMA to further gauge the trend. 8 | We buy when price is above the 13-period exponential moving average, and both EMA and Bear Power is increasing. 9 | We sell when price is below the 13 period exponential moving average, and both EMA and Bull Power is decreasing. 10 | 11 | Author: Cheryl 12 | ''' 13 | class ElderRayAlternative: 14 | def __init__(self, file_path): 15 | self.max_window = 100 # set to 100 for better understanding of trend in graph. MIN value 14 16 | self.df = pd.read_csv(file_path)[-self.max_window:] 17 | self.high = self.df['high'] 18 | self.close = self.df['close'] 19 | self.low = self.df['low'] 20 | 21 | def calculate_ema(self): 22 | # initialise EMA indicator for 13 time periods 23 | indicator_ema = ta.trend.EMAIndicator(close = self.close, n = 13) 24 | # Calculate 25 | self.df['13_ema'] = indicator_ema.ema_indicator() 26 | 27 | def calculate_bull_power(self): 28 | self.df['bull_power'] = self.df['high'] - self.df['13_ema'] 29 | 30 | def calculate_bear_power(self): 31 | self.df['bear_power'] = self.df['low'] - self.df['13_ema'] 32 | 33 | 34 | def determine_signal(self, df): 35 | action = 0 36 | ema_dist = df['close'].iloc[-1] - df['13_ema'].iloc[-1] 37 | 38 | # BUY CRITERIA: price is above 13-EMA and both EMA and Bear Power is increasing 39 | if df['close'].iloc[-1] > df['13_ema'].iloc[-1] and df['13_ema'].iloc[-1] > df['13_ema'].iloc[-2] and df['bear_power'].iloc[-1] > df['bear_power'].iloc[-2]: 40 | action = 1 41 | 42 | # SELL CRITERIA: price is below 13-EMA and both EMA and Bull Power is decreasing 43 | elif df['close'].iloc[-1] < df['13_ema'].iloc[-1] and df['13_ema'].iloc[-1] < df['13_ema'].iloc[-2] and df['bull_power'].iloc[-1] < df['bull_power'].iloc[-2]: 44 | action = -1 45 | 46 | return (action, ema_dist) 47 | 48 | def run_elder_ray(self): 49 | self.calculate_ema() 50 | self.calculate_bull_power() 51 | self.calculate_bear_power() 52 | signal = self.determine_signal(self.df) 53 | return signal, self.df 54 | 55 | ''' The following methods are for plotting ''' 56 | 57 | def find_all_signals(self, plot_df): 58 | # assign intitial value of hold 59 | plot_df['signal'] = 0 60 | 61 | start = -1*len(plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 62 | end = start + 14 # where the current window will stop (exclusive of the element at this index) 63 | 64 | # loop through data to determine all signals 65 | while end < 0: 66 | curr_window = plot_df[start:end] 67 | action = self.determine_signal(curr_window)[0] 68 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 69 | end += 1 70 | start += 1 71 | 72 | # compute final signal 73 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-22:])[0] 74 | 75 | 76 | def plot_graph(self): 77 | 78 | # deep copy of data so original is not impacted 79 | plot_df = self.df.copy(deep=True) 80 | 81 | # determine all signals for the dataset 82 | self.find_all_signals(plot_df) 83 | 84 | # zero line series for horizontal axis at value 0 in bull power and bear power 85 | plot_df['zero_line'] = 0 86 | 87 | # initialise visualisation object for plotting 88 | visualisation = v.Visualise(plot_df) 89 | 90 | # determining one buy signal example for plotting 91 | visualisation.determine_buy_marker() 92 | 93 | # determining one sell signal example for plotting 94 | visualisation.determine_sell_marker() 95 | 96 | # add subplots of 13 ema, bull power, bear power, and zero lines 97 | visualisation.add_subplot(plot_df['13_ema'], color="royalblue") 98 | visualisation.add_subplot(plot_df['bull_power'], panel=1, type="bar", ylabel="Bull Power") 99 | visualisation.add_subplot(plot_df['bear_power'], panel=2, type="bar", ylabel="Bear Power") 100 | visualisation.add_subplot(plot_df['zero_line'], panel=1, secondary_y=False, color="gray") 101 | visualisation.add_subplot(plot_df['zero_line'], panel=2, secondary_y=False, color="gray") 102 | 103 | # create final plot with title 104 | visualisation.plot_graph("Elder Ray and 13 EMA Strategy") -------------------------------------------------------------------------------- /trading_strategies/elder_ray_sma.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import mplfinance as mpf 4 | import numpy as np 5 | 6 | ''' 7 | The Elder Ray Index measures the amount of buying and selling power in a market with two indicators: Bull Power and Bear Power. 8 | This index is used in conjunction with a 13 EMA and 5 SMAto further gauge the trend. 9 | We buy when Bear Power is negative but increasing, Bull Power is increasing, EMA is positively sloped and close price is above SMA. 10 | We sell when Bull Power is positive but decreasing, Bear Power is decreasing, EMA is negatively sloped and close price is below SMA. 11 | 12 | Author: Cheryl 13 | ''' 14 | class ElderRaySma: 15 | def __init__(self, file_path): 16 | self.max_window = 100 # set to 100 for better understanding of trend in graph. MIN value 14 17 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos"))[-self.max_window:] 18 | self.high = self.df['high'] 19 | self.close = self.df['close'] 20 | self.low = self.df['low'] 21 | 22 | def calculate_ema(self): 23 | # initialise EMA indicator for 13 time periods 24 | indicator_ema = ta.trend.EMAIndicator(close = self.close, window = 13) 25 | # Calculate 26 | self.df['13_ema'] = indicator_ema.ema_indicator() 27 | 28 | def calculate_sma(self): 29 | # initialise SMA indicator for 5 time periods 30 | indicator_sma = ta.trend.SMAIndicator(close = self.close, window = 5) 31 | # Calculate 5 sma 32 | self.df['5_sma'] = indicator_sma.sma_indicator() 33 | 34 | def calculate_bull_power(self): 35 | self.df['bull_power'] = self.df['high'] - self.df['13_ema'] 36 | 37 | def calculate_bear_power(self): 38 | self.df['bear_power'] = self.df['low'] - self.df['13_ema'] 39 | 40 | 41 | def determine_signal(self): 42 | action = 0 43 | ema_dist = self.df['close'].iloc[-1] - self.df['13_ema'].iloc[-1] 44 | 45 | # BUY CRITERIA: Bear power’s value is negative but increasing, Bull power’s value is increasing and 13 EMA is increasing. AND price is greater than 5 sma 46 | if self.df['bear_power'].iloc[-1] < 0 and self.df['bear_power'].iloc[-1] > self.df['bear_power'].iloc[-2] \ 47 | and self.df['bull_power'].iloc[-1] > self.df['bull_power'].iloc[-2] and self.df['13_ema'].iloc[-1] > self.df['13_ema'].iloc[-2] \ 48 | and self.df['close'].iloc[-1] > self.df['5_sma'].iloc[-1]: 49 | action = 1 50 | 51 | # SELL CRITERIA: Bull power’s value is positive but decreasing, Bear power’s value is decreasing and 13 EMA is decreasing. AND price is less than 5 sma 52 | elif self.df['bull_power'].iloc[-1] > 0 and self.df['bull_power'].iloc[-1] < self.df['bull_power'].iloc[-2] \ 53 | and self.df['bear_power'].iloc[-1] < self.df['bear_power'].iloc[-2] and self.df['13_ema'].iloc[-1] < self.df['13_ema'].iloc[-2] \ 54 | and self.df['close'].iloc[-1] < self.df['5_sma'].iloc[-1]: 55 | action = -1 56 | 57 | return (action, ema_dist) 58 | 59 | def run_elder_ray(self): 60 | self.calculate_sma() 61 | self.calculate_ema() 62 | self.calculate_bull_power() 63 | self.calculate_bear_power() 64 | signal = self.determine_signal() 65 | return signal, self.df -------------------------------------------------------------------------------- /trading_strategies/ema_3.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import trading_strategies.visualise as v 3 | 4 | # @author: vita 5 | class ThreeEma: 6 | 7 | def __init__(self, file_path): 8 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos")) 9 | self.close = self.df['close'] 10 | 11 | def exponential_moving_average_5(self): 12 | self.df['5ema'] = self.df['close'].ewm(span=5, adjust=False).mean() 13 | 14 | def exponential_moving_average_20(self): 15 | self.df['20ema'] = self.df['close'].ewm(span=20, adjust=False).mean() 16 | 17 | def exponential_moving_average_50(self): 18 | self.df['50ema'] = self.df['close'].ewm(span=50, adjust=False).mean() 19 | 20 | def determine_signal(self,dframe): 21 | action = 0 # hold 22 | 23 | close = dframe['close'] 24 | ema_5 = dframe['5ema'] 25 | ema_20 = dframe['20ema'] 26 | ema_50 = dframe['50ema'] 27 | 28 | # SELL CRITERIA: 5ema has crossed down under 20ema such that 5ema < 20ema < 50ema and closing price < 50 ema 29 | if (close.iloc[-1] < ema_50.iloc[-1]) and (ema_5.iloc[-3] > ema_20.iloc[-3]) and ( 30 | ema_5.iloc[-2] < ema_20.iloc[-2] < ema_50.iloc[-2]): 31 | action = -1 32 | # BUY CRITERIA: 5ema has crossed over 20ema such that 5ema > 20ema > 50ema and closing price is > 50 ema 33 | elif (close.iloc[-1] > ema_50.iloc[-1]) and (ema_5.iloc[-3] < ema_20.iloc[-3]) and ( 34 | ema_5.iloc[-2] > ema_20.iloc[-2] > ema_50.iloc[-2]): 35 | action = 1 36 | 37 | return action, ema_50.iloc[-1] - ema_20.iloc[-1], 38 | 39 | def run_ema_3(self): 40 | self.exponential_moving_average_5() 41 | self.exponential_moving_average_20() 42 | self.exponential_moving_average_50() 43 | signal = self.determine_signal(self.df) 44 | return signal, self.df 45 | 46 | ''' The following methods are for plotting ''' 47 | def find_all_signals(self, plot_df): 48 | # assign initial value of hold 49 | plot_df['signal'] = 0 50 | 51 | start = -1 * len(plot_df) 52 | end = start + 3 53 | 54 | # loop through data to determine all signals 55 | while end < 0: 56 | curr_window = plot_df[start:end] 57 | action = self.determine_signal(curr_window)[0] 58 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 59 | end += 1 60 | start += 1 61 | 62 | action = self.determine_signal(plot_df[-3:])[0] 63 | plot_df.loc[plot_df.index[-1], 'signal'] = action 64 | 65 | def plot_graph(self): 66 | # create shallow copy for plotting so as to not accidentally impact original df 67 | plot_df = self.df.copy(deep=False) 68 | 69 | self.find_all_signals(plot_df) 70 | 71 | # initialise visualisation object for plotting 72 | visualisation = v.Visualise(plot_df) 73 | 74 | # determining one buy signal example for plotting 75 | visualisation.determine_buy_marker() 76 | 77 | # determining one sell signal example for plotting 78 | visualisation.determine_sell_marker() 79 | 80 | # add subplots 81 | visualisation.add_subplot(plot_df['5ema'], color='turquoise', width=0.75) 82 | visualisation.add_subplot(plot_df['20ema'], color='violet', width=0.75) 83 | visualisation.add_subplot(plot_df['50ema'], color='orange', width=0.75) 84 | 85 | # create final plot with title 86 | visualisation.plot_graph("Three EMA Strategy") 87 | 88 | -------------------------------------------------------------------------------- /trading_strategies/ema_3_alternative.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import trading_strategies.visualise as v 3 | # @author: vita 4 | class ThreeEma: 5 | 6 | def __init__(self, file_path): 7 | self.df = pd.read_csv(file_path) 8 | # self.df = pd.DataFrame(inputs) 9 | self.close = self.df['close'] 10 | 11 | def exponential_moving_average_5(self): 12 | self.df['5ema'] = self.df['close'].ewm(span=5, adjust=False).mean() 13 | 14 | def exponential_moving_average_20(self): 15 | self.df['20ema'] = self.df['close'].ewm(span=20, adjust=False).mean() 16 | 17 | def exponential_moving_average_50(self): 18 | self.df['50ema'] = self.df['close'].ewm(span=50, adjust=False).mean() 19 | 20 | def determine_signal(self, dframe): 21 | action = 0 #hold 22 | 23 | close = dframe['close'] 24 | ema_5 = dframe['5ema'] 25 | ema_20 = dframe['20ema'] 26 | ema_50 = dframe['50ema'] 27 | 28 | # SELL CRITERIA: 5ema has crossed down under 20ema such that 5ema < 20ema < 50ema and closing price < 50 ema 29 | if(close.iloc[-1] < ema_50.iloc[-1]) and (ema_5.iloc[-1] 20ema > 50ema and closing price is > 50 ema 32 | elif (self.close.iloc[-1] > ema_50.iloc[-1]) and (ema_5.iloc[-1]>ema_20.iloc[-1]>ema_50.iloc[-1]): 33 | action = 1 34 | 35 | return action, ema_50.iloc[-1]-ema_20.iloc[-1], 36 | 37 | def run_ema_3(self): 38 | self.exponential_moving_average_5() 39 | self.exponential_moving_average_20() 40 | self.exponential_moving_average_50() 41 | signal = self.determine_signal(self.df) 42 | return signal, self.df 43 | 44 | 45 | ''' The following methods are for plotting ''' 46 | def find_all_signals(self, plot_df): 47 | # assign initial value of hold 48 | plot_df['signal'] = 0 49 | 50 | start = -1 * len(plot_df) 51 | end = start + 3 52 | 53 | # loop through data to determine all signals 54 | while end < 0: 55 | curr_window = plot_df[start:end] 56 | action = self.determine_signal(curr_window)[0] 57 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 58 | end += 1 59 | start += 1 60 | 61 | action = self.determine_signal(plot_df[-3:])[0] 62 | plot_df.loc[plot_df.index[-1], 'signal'] = action 63 | 64 | def plot_graph(self): 65 | # create shallow copy for plotting so as to not accidentally impact original df 66 | plot_df = self.df.copy(deep=False) 67 | 68 | self.find_all_signals(plot_df) 69 | 70 | # initialise visualisation object for plotting 71 | visualisation = v.Visualise(plot_df) 72 | 73 | # determining one buy signal example for plotting 74 | visualisation.determine_buy_marker() 75 | 76 | # determining one sell signal example for plotting 77 | visualisation.determine_sell_marker() 78 | 79 | # add subplots 80 | visualisation.add_subplot(plot_df['5ema'], color='turquoise', width=0.75) 81 | visualisation.add_subplot(plot_df['20ema'], color='violet', width=0.75) 82 | visualisation.add_subplot(plot_df['50ema'], color='orange', width=0.75) 83 | 84 | # create final plot with title 85 | visualisation.plot_graph("Three EMA Strategy") 86 | 87 | -------------------------------------------------------------------------------- /trading_strategies/ema_crossover.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import trading_strategies.visualise as v 3 | 4 | ''' 5 | ### Author: Wilson and Vita ### 6 | Strategy from: 7 | https://forexwithanedge.com/ema-trading-strategy/ 8 | This strategy looks for crosses in the 50 and 20 ema and places a position based on a crosses 9 | 10 | ''' 11 | 12 | class EMACrossover: 13 | 14 | #constructor 15 | def __init__(self, file_path): 16 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos")) 17 | 18 | def add_20_ema(self): 19 | self.df['20ema'] = self.df['close'].ewm(span=20, adjust=False).mean() 20 | 21 | def add_50_ema(self): 22 | self.df['50ema'] = self.df['close'].ewm(span=50, adjust=False).mean() 23 | 24 | def determine_signal(self, dframe): 25 | action = 0 26 | ema_20 = dframe['20ema'] 27 | ema_50 = dframe['50ema'] 28 | close = dframe['close'] 29 | 30 | #buy if 20 ema crosses above 50 ema 31 | if (ema_20.iloc[-2] < ema_50.iloc[-2]) and (ema_20.iloc[-1] > ema_50.iloc[-1]): 32 | action = 1 33 | 34 | #sell if 20 ema crosses below 50 ema 35 | if (ema_20.iloc[-2] > ema_50.iloc[-2]) and (ema_20.iloc[-1] < ema_50.iloc[-1]): 36 | action = -1 37 | 38 | return action, ema_20.iloc[-1] - close.iloc[-1] 39 | 40 | def run_ema_crossover(self): 41 | self.add_20_ema() 42 | self.add_50_ema() 43 | return self.determine_signal(self.df), self.df 44 | 45 | ''' The following methods are for plotting ''' 46 | def find_all_signals(self, plot_df): 47 | # assign initial value of hold 48 | plot_df['signal'] = 0 49 | 50 | start = -1 * len(plot_df) 51 | end = start + 3 52 | 53 | # loop through data to determine all signals 54 | while end < 0: 55 | curr_window = plot_df[start:end] 56 | action = self.determine_signal(curr_window)[0] 57 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 58 | end += 1 59 | start += 1 60 | 61 | action = self.determine_signal(plot_df[-2:])[0] 62 | plot_df.loc[plot_df.index[-1], 'signal'] = action 63 | 64 | 65 | def plot_graph(self): 66 | # create shallow copy for plotting so as to not accidentally impact original df 67 | plot_df = self.df.copy(deep=False) 68 | 69 | self.find_all_signals(plot_df) 70 | 71 | # initialise visualisation object for plotting 72 | visualisation = v.Visualise(plot_df) 73 | 74 | # determining one buy signal example for plotting 75 | visualisation.determine_buy_marker() 76 | 77 | # determining one sell signal example for plotting 78 | visualisation.determine_sell_marker() 79 | 80 | # add subplots 81 | visualisation.add_subplot(plot_df['20ema'], color='violet', width=0.75) 82 | visualisation.add_subplot(plot_df['50ema'], color='orange', width=0.75) 83 | 84 | # create final plot with title 85 | visualisation.plot_graph("EMA Crossover Strategy") 86 | 87 | -------------------------------------------------------------------------------- /trading_strategies/ema_crossover_alternative.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import trading_strategies.visualise as v 3 | 4 | ''' 5 | ### Author: Wilson and Vita ### 6 | Strategy from: 7 | https://forexwithanedge.com/ema-trading-strategy/ 8 | This strategy looks for crosses in the 50 and 20 ema and places a position based on a crosses 9 | 10 | ''' 11 | 12 | class EMACrossover: 13 | 14 | #constructor 15 | def __init__(self, file_path): 16 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos")) 17 | # self.df = pd.DataFrame(inputs) 18 | 19 | def add_20_ema(self): 20 | self.df['20ema'] = self.df['close'].ewm(span=20, adjust=False).mean() 21 | 22 | def add_50_ema(self): 23 | self.df['50ema'] = self.df['close'].ewm(span=50, adjust=False).mean() 24 | 25 | def add_distance_between_20ema_and_50ema(self): 26 | self.df['distance'] = self.df['20ema'] - self.df['50ema'] 27 | 28 | def determine_signal(self, dframe): 29 | action = 0 30 | ema_20 = dframe['20ema'] 31 | ema_50 = dframe['50ema'] 32 | close = dframe['close'] 33 | 34 | # buy if 20 ema crosses above 50 ema 35 | if (ema_20.iloc[-1] > ema_50.iloc[-1]): 36 | action = 1 37 | 38 | # sell if 20 ema crosses below 50 ema 39 | if (ema_20.iloc[-1] < ema_50.iloc[-1]): 40 | action = -1 41 | 42 | return action, ema_20.iloc[-1] - close.iloc[-1] 43 | 44 | def run_ema_crossover(self): 45 | self.add_20_ema() 46 | self.add_50_ema() 47 | return self.determine_signal(self.df), self.df 48 | 49 | ''' The following methods are for plotting ''' 50 | 51 | def find_all_signals(self, plot_df): 52 | # assign initial value of hold 53 | plot_df['signal'] = 0 54 | 55 | start = -1 * len(plot_df) 56 | end = start + 2 57 | 58 | # loop through data to determine all signals 59 | while end < 0: 60 | curr_window = plot_df[start:end] 61 | action = self.determine_signal(curr_window)[0] 62 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 63 | end += 1 64 | start += 1 65 | 66 | action = self.determine_signal(plot_df[-2:])[0] 67 | plot_df.loc[plot_df.index[-1], 'signal'] = action 68 | 69 | def plot_graph(self): 70 | # create shallow copy for plotting so as to not accidentally impact original df 71 | plot_df = self.df.copy(deep=False) 72 | 73 | self.find_all_signals(plot_df) 74 | 75 | plot_df = plot_df[100:200] 76 | 77 | # initialise visualisation object for plotting 78 | visualisation = v.Visualise(plot_df) 79 | 80 | # determining one buy signal example for plotting 81 | visualisation.determine_buy_marker() 82 | 83 | # determining one sell signal example for plotting 84 | visualisation.determine_sell_marker() 85 | 86 | # add subplots 87 | visualisation.add_subplot(plot_df['20ema'], color='violet', width=0.75) 88 | visualisation.add_subplot(plot_df['50ema'], color='orange', width=0.75) 89 | 90 | # create final plot with title 91 | visualisation.plot_graph("EMA Crossover Strategy Alternative") 92 | 93 | 94 | # # strategy = EMACrossover(r"C:\Users\Wilson\Documents\INFO3600\USD_JPY_M15.csv") 95 | # strategy = EMACrossover('/Users/vhuang/INFO3600/FXCM_EUR_USD_H4_1.csv') 96 | # print(strategy.run_ema_crossover()) 97 | # strategy.plot_graph() 98 | 99 | -------------------------------------------------------------------------------- /trading_strategies/ema_crossover_macd.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: vita 4 | This strategy uses the crossover between 9EMA and 21EMA with MACD histogram as confirmation to avoid false signals 5 | http://www.forexfunction.com/trading-strategy-of-ema-crossover-with-macd 6 | """ 7 | import pandas as pd 8 | import ta 9 | import trading_strategies.visualise as v 10 | 11 | class EMACrossoverMACD: 12 | 13 | def __init__(self, file_path): 14 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos")) 15 | 16 | def calculate_21_ema(self): 17 | self.df['21ema'] = self.df['close'].ewm(span=21, adjust=False).mean() 18 | 19 | def calculate_9_ema(self): 20 | self.df['9ema'] = self.df['close'].ewm(span=9, adjust=False).mean() 21 | 22 | def add_macd_diff(self): 23 | self.df['macd_histogram'] = ta.trend.MACD(close = self.df['close']).macd_diff() 24 | 25 | def determine_signal(self, dframe): 26 | ema9 = dframe['9ema'] 27 | ema21 = dframe['21ema'] 28 | histogram = dframe['macd_histogram'] 29 | close = dframe['close'] 30 | 31 | action = 0 32 | 33 | # SELL CRITERIA: 9EMA crosses below 21EMA followed by a MACD histogram crossover into negatives 34 | if (ema9.iloc[-2] < ema21.iloc[-2] and ema9.iloc[-3]>ema21.iloc[-3]) and ((histogram.iloc[-1] < 0 and histogram.iloc[-2] > 0) or (histogram.iloc[-1] > 0 and histogram.iloc[-2] < 0)): 35 | action = -1 36 | 37 | # BUY CRITERIA: 9EMA crosses above 21EMA followed by a MACD histogram crossover ito positives 38 | if (ema9.iloc[-2] > ema21.iloc[-2] and ema9.iloc[-3] 0 and histogram.iloc[-2] < 0) or (histogram.iloc[-1] < 0 and histogram.iloc[-2] > 0)): 39 | action = 1 40 | 41 | return action, ema21.iloc[-1]-close.iloc[-1], 42 | 43 | def run_ema_crossover_macd(self): 44 | self.calculate_21_ema() 45 | self.calculate_9_ema() 46 | self.add_macd_diff() 47 | return self.determine_signal(self.df), self.df 48 | 49 | ''' The following methods are for plotting ''' 50 | 51 | def find_all_signals(self, plot_df): 52 | # assign intitial value of hold 53 | plot_df['signal'] = 0 54 | 55 | start = -1 * len(plot_df) 56 | end = start + 36 57 | 58 | # loop through data to determine all signals 59 | while end < 0: 60 | curr_window = plot_df[start:end] 61 | action = self.determine_signal(curr_window)[0] 62 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 63 | end += 1 64 | start += 1 65 | 66 | action = self.determine_signal(plot_df[-36:])[0] 67 | plot_df.loc[plot_df.index[-1], 'signal'] = action 68 | 69 | def plot_graph(self): 70 | # create shallow copy for plotting so as to not accidentally impact original df 71 | plot_df = self.df.copy(deep=False) 72 | self.find_all_signals(plot_df) 73 | 74 | # initialise visualisation object for plotting 75 | visualisation = v.Visualise(plot_df) 76 | 77 | # determining one buy signal example for plotting 78 | visualisation.determine_buy_marker() 79 | 80 | # determining one sell signal example for plotting 81 | visualisation.determine_sell_marker() 82 | 83 | # add subplots 84 | visualisation.add_subplot(plot_df['macd_histogram'], panel=1, color='b', type='bar') 85 | visualisation.add_subplot(plot_df['9ema'], panel=0, color='pink', width=0.75) 86 | visualisation.add_subplot(plot_df['21ema'], panel=0, color='turquoise', width=0.75) 87 | 88 | # create final plot with title 89 | visualisation.plot_graph("EMA Crossover with MACD Strategy") 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /trading_strategies/ema_crossover_rsi.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: vita 4 | This strategy uses the crossover between 6EMA and 12EMA with RSI 5 | http://www.forexfunction.com/ema-crossover-and-rsi-simple-trading-strategy 6 | """ 7 | import datetime 8 | import pandas as pd 9 | import trading_strategies.visualise as v 10 | import ta 11 | 12 | 13 | 14 | class EMACrossoverRSI: 15 | 16 | def __init__(self, file_path): 17 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos")) 18 | self.close = self.df['close'] 19 | 20 | def calculate_12_ema(self): 21 | self.df['12ema'] = self.df['close'].ewm(span=12, adjust=False).mean() 22 | 23 | def calculate_6_ema(self): 24 | self.df['6ema'] = self.df['close'].ewm(span=6, adjust=False).mean() 25 | 26 | def add_rsi(self): 27 | rsi_ind = ta.momentum.RSIIndicator(close=self.close, window=14) 28 | self.df['rsi'] = rsi_ind.rsi() 29 | 30 | def determine_signal(self, dframe): 31 | ema6 = dframe['6ema'] 32 | ema12 = dframe['12ema'] 33 | rsi = dframe['rsi'] 34 | close = dframe['close'] 35 | 36 | action = 0 37 | 38 | # SELL CRITERIA: when 6EMA crosses above 12EMA and RSI value has crossed below 50 39 | if (ema6.iloc[-1] < ema12.iloc[-1] and ema6.iloc[-2] > ema12.iloc[-2]) and ( 40 | 41 | rsi.iloc[-1] < 50 and rsi.iloc[-2] > 50): 42 | action = -1 43 | 44 | # BUY CRITERIA: when 6EMA crosses below 12EMA and RSI value has crossed above 50 45 | if (ema6.iloc[-1] > ema12.iloc[-1] and ema6.iloc[-2] < ema12.iloc[-2]) and ( 46 | rsi.iloc[-1] > 50 and rsi.iloc[-2] < 50): 47 | action = 1 48 | 49 | return (action, ema12.iloc[-1] - close.iloc[-1],) 50 | 51 | def run_ema_crossover_rsi(self): 52 | self.calculate_12_ema() 53 | self.calculate_6_ema() 54 | self.add_rsi() 55 | return self.determine_signal(self.df), self.df 56 | 57 | ''' The following methods are for plotting ''' 58 | def find_all_signals(self, plot_df): 59 | # assign initial value of hold 60 | plot_df['signal'] = 0 61 | 62 | start = -1 * len(plot_df) 63 | end = start + 15 64 | 65 | # loop through data to determine all signals 66 | while end < 0: 67 | curr_window = plot_df[start:end] 68 | action = self.determine_signal(curr_window)[0] 69 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 70 | end += 1 71 | start += 1 72 | 73 | action = self.determine_signal(plot_df[-15:])[0] 74 | plot_df.loc[plot_df.index[-1], 'signal'] = action 75 | 76 | def plot_graph(self): 77 | # create shallow copy for plotting so as to not accidentally impact original df 78 | plot_df = self.df.copy(deep=False) 79 | 80 | self.find_all_signals(plot_df) 81 | 82 | # preparing values for horizontal line at rsi values 80 and 20 for visual purposes 83 | plot_df['50_line'] = 50 84 | 85 | # initialise visualisation object for plotting 86 | visualisation = v.Visualise(plot_df) 87 | 88 | # determining one buy signal example for plotting 89 | visualisation.determine_buy_marker() 90 | 91 | # determining one sell signal example for plotting 92 | visualisation.determine_sell_marker() 93 | 94 | # add subplots of RSI, 80 line and 20 line 95 | visualisation.add_subplot(plot_df['6ema'], panel=0, color='pink', width=0.75) 96 | visualisation.add_subplot(plot_df['12ema'], panel=0, color='b', width=0.75) 97 | visualisation.add_subplot(plot_df['rsi'], panel=1, color='m', width=0.75, ylabel='RSI') 98 | visualisation.add_subplot(plot_df['50_line'], panel=1, color='k', secondary_y=False, width=0.75, linestyle='solid') 99 | 100 | # create final plot with title 101 | visualisation.plot_graph("EMA Crossover with RSI strategy") 102 | 103 | 104 | # # strategy = EMACrossoverRSI('/Users/vhuang/INFO3600/USD_JPY_M15.csv') 105 | # # strategy = ThreeCrowSoldiersCandlesticksRsi('/Users/vhuang/INFO3600/FXCM_EUR_USD_H4_1.csv') 106 | # strategy = EMACrossoverRSI('/Users/vhuang/INFO3600/FXCM_EUR_USD_H4.csv') 107 | # signal = strategy.run_ema_crossover_rsi() 108 | # print(signal) 109 | # print(strategy.plot_graph()) 110 | -------------------------------------------------------------------------------- /trading_strategies/ema_crossover_rsi_alternative.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: vita 4 | This strategy uses the crossover between 6EMA and 12EMA with RSI 5 | http://www.forexfunction.com/ema-crossover-and-rsi-simple-trading-strategy 6 | """ 7 | import datetime 8 | import pandas as pd 9 | import ta 10 | import trading_strategies.visualise as v 11 | 12 | 13 | class EMACrossoverRSI: 14 | 15 | def __init__(self, file_path): 16 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos")) 17 | # self.df = pd.DataFrame(inputs) 18 | self.close = self.df['close'] 19 | 20 | def calculate_12_ema(self): 21 | self.df['12ema'] = self.df['close'].ewm(span=12, adjust=False).mean() 22 | 23 | def calculate_6_ema(self): 24 | self.df['6ema'] = self.df['close'].ewm(span=6, adjust=False).mean() 25 | 26 | def add_rsi(self): 27 | rsi_ind = ta.momentum.RSIIndicator(close=self.close, window=14) 28 | self.df['rsi'] = rsi_ind.rsi() 29 | 30 | def add_distance_between_ema12_close(self): 31 | self.df['distance'] = self.df['12ema'] - self.close 32 | 33 | def determine_signal(self, dframe): 34 | ema6 = dframe['6ema'] 35 | ema12 = dframe['12ema'] 36 | rsi = dframe['rsi'] 37 | close = dframe['close'] 38 | 39 | action = 0 40 | 41 | # SELL CRITERIA: when 6EMA crosses above 12EMA and RSI value has crossed below 50 42 | if (ema6.iloc[-1] < ema12.iloc[-1]) and ( 43 | 44 | rsi.iloc[-1] < 50 ): 45 | action = -1 46 | 47 | # BUY CRITERIA: when 6EMA crosses below 12EMA and RSI value has crossed above 50 48 | if (ema6.iloc[-1] > ema12.iloc[-1] ) and ( 49 | rsi.iloc[-1] > 50): 50 | action = 1 51 | 52 | return (action, ema12.iloc[-1] - close.iloc[-1],) 53 | 54 | def run_ema_crossover_rsi(self): 55 | self.calculate_12_ema() 56 | self.calculate_6_ema() 57 | self.add_rsi() 58 | return self.determine_signal(self.df), self.df 59 | 60 | ''' The following methods are for plotting ''' 61 | 62 | def find_all_signals(self, plot_df): 63 | # assign initial value of hold 64 | plot_df['signal'] = 0 65 | 66 | start = -1 * len(plot_df) 67 | end = start + 14 68 | 69 | # loop through data to determine all signals 70 | while end < 0: 71 | curr_window = plot_df[start:end] 72 | action = self.determine_signal(curr_window)[0] 73 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 74 | end += 1 75 | start += 1 76 | 77 | action = self.determine_signal(plot_df[-15:])[0] 78 | plot_df.loc[plot_df.index[-1], 'signal'] = action 79 | 80 | def plot_graph(self): 81 | # create shallow copy for plotting so as to not accidentally impact original df 82 | plot_df = self.df.copy(deep=False) 83 | 84 | self.find_all_signals(plot_df) 85 | 86 | plot_df = plot_df[140:240] 87 | 88 | # preparing values for horizontal line at rsi values 80 and 20 for visual purposes 89 | plot_df['50_line'] = 50 90 | 91 | # initialise visualisation object for plotting 92 | visualisation = v.Visualise(plot_df) 93 | 94 | # determining one buy signal example for plotting 95 | visualisation.determine_buy_marker() 96 | 97 | # determining one sell signal example for plotting 98 | visualisation.determine_sell_marker() 99 | 100 | # add subplots of RSI, 80 line and 20 line 101 | visualisation.add_subplot(plot_df['6ema'], panel=0, color='pink', width=0.75) 102 | visualisation.add_subplot(plot_df['12ema'], panel=0, color='b', width=0.75) 103 | visualisation.add_subplot(plot_df['rsi'], panel=1, color='m', width=0.75, ylabel='RSI') 104 | visualisation.add_subplot(plot_df['50_line'], panel=1, color='k', secondary_y=False, width=0.75, 105 | linestyle='solid') 106 | 107 | # create final plot with title 108 | visualisation.plot_graph("EMA Crossover with RSI Strategy Alternative") 109 | 110 | 111 | -------------------------------------------------------------------------------- /trading_strategies/ema_macd_rsi.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import trading_strategies.visualise as v 4 | 5 | # @author: vita 6 | 7 | ''' 8 | This strategy combines ema_crossover_rsi_alternative and a modified ema_crossover_macd to determine buy and sell 9 | signals. Ema_crossover_macd was modified such that 9EMA only needs to be below/above 21EMA to fulfill sell/buy 10 | signals respectively rather than a crossover below or above. 11 | ''' 12 | class EMAMACDRSI: 13 | 14 | def __init__(self, file_path): 15 | self.df = pd.read_csv(file_path) 16 | # self.df = pd.DataFrame(inputs) 17 | self.close = self.df['close'] 18 | 19 | def calculate_21_ema(self): 20 | self.df['21ema'] = self.df['close'].ewm(span=21, adjust=False).mean() 21 | 22 | def calculate_9_ema(self): 23 | self.df['9ema'] = self.df['close'].ewm(span=9, adjust=False).mean() 24 | 25 | def add_macd_diff(self): 26 | self.df['macd_histogram'] = ta.trend.MACD(close = self.close).macd_diff() 27 | 28 | def add_distance_between_21ema_and_close(self): 29 | self.df['distance'] = self.df['21ema'] - self.close 30 | 31 | def add_rsi(self): 32 | rsi_ind = ta.momentum.RSIIndicator(close=self.close, n=14) 33 | self.df['rsi'] = rsi_ind.rsi() 34 | 35 | def calculate_12_ema(self): 36 | self.df['12ema'] = self.df['close'].ewm(span=12, adjust=False).mean() 37 | 38 | def calculate_6_ema(self): 39 | self.df['6ema'] = self.df['close'].ewm(span=6, adjust=False).mean() 40 | 41 | def determine_signal(self, dframe): 42 | ema9 = dframe['9ema'] 43 | ema21 = dframe['21ema'] 44 | histogram = dframe['macd_histogram'] 45 | close = dframe['close'] 46 | ema6 = dframe['6ema'] 47 | ema12 = dframe['12ema'] 48 | rsi = dframe['rsi'] 49 | 50 | action = 0 51 | 52 | # SELL CRITERIA: 9EMA crosses below 21EMA followed by a MACD histogram crossover into negatives 53 | if ((ema9.iloc[-2] < ema21.iloc[-2]) and ( 54 | (histogram.iloc[-1] < 0 and histogram.iloc[-2] > 0) or (histogram.iloc[-1] > 0 and histogram.iloc[-2] < 0))) \ 55 | or ((ema6.iloc[-1] < ema12.iloc[-1]) and (rsi.iloc[-1] < 50)): 56 | action = -1 57 | 58 | # BUY CRITERIA: 9EMA crosses above 21EMA followed by a MACD histogram crossover ito positives 59 | if ((ema9.iloc[-2] > ema21.iloc[-2]) and ( 60 | (histogram.iloc[-1] > 0 and histogram.iloc[-2] < 0) or (histogram.iloc[-1] < 0 and histogram.iloc[-2] > 0))) \ 61 | or ((ema6.iloc[-1] > ema12.iloc[-1]) and (rsi.iloc[-1] > 50)): 62 | action = 1 63 | 64 | return action, close.iloc[-1]-ema9.iloc[-1] 65 | 66 | def run_ema_macd_rsi(self): 67 | self.calculate_21_ema() 68 | self.calculate_9_ema() 69 | self.add_macd_diff() 70 | self.calculate_12_ema() 71 | self.calculate_6_ema() 72 | self.add_rsi() 73 | return self.determine_signal(self.df), self.df 74 | 75 | ''' The following methods are for plotting ''' 76 | 77 | def find_all_signals(self, plot_df): 78 | # assign initial value of hold 79 | plot_df['signal'] = 0 80 | 81 | start = -1 * len(plot_df) 82 | end = start + 36 83 | 84 | # loop through data to determine all signals 85 | while end < 0: 86 | curr_window = plot_df[start:end] 87 | action = self.determine_signal(curr_window)[0] 88 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 89 | end += 1 90 | start += 1 91 | 92 | action = self.determine_signal(plot_df[-15:])[0] 93 | plot_df.loc[plot_df.index[-1], 'signal'] = action 94 | 95 | def plot_graph(self): 96 | # create shallow copy for plotting so as to not accidentally impact original df 97 | plot_df = self.df.copy(deep=False) 98 | 99 | self.find_all_signals(plot_df) 100 | 101 | plot_df = plot_df[120:220] 102 | 103 | # preparing values for horizontal line at rsi values 80 and 20 for visual purposes 104 | plot_df['50_line'] = 50 105 | 106 | # initialise visualisation object for plotting 107 | visualisation = v.Visualise(plot_df) 108 | 109 | # determining one buy signal example for plotting 110 | visualisation.determine_buy_marker() 111 | 112 | # determining one sell signal example for plotting 113 | visualisation.determine_sell_marker() 114 | 115 | # add subplots of RSI, 80 line and 20 line 116 | visualisation.add_subplot(plot_df['6ema'], panel=0, color='pink', width=0.75) 117 | visualisation.add_subplot(plot_df['12ema'], panel=0, color='b', width=0.75) 118 | visualisation.add_subplot(plot_df['rsi'], panel=1, color='m', width=0.75, ylabel='RSI') 119 | visualisation.add_subplot(plot_df['50_line'], panel=1, color='k', secondary_y=False, width=0.75, 120 | linestyle='solid') 121 | visualisation.add_subplot(plot_df['macd_histogram'], panel=2, color='b', type='bar') 122 | visualisation.add_subplot(plot_df['9ema'], panel=0, color='orange', width=0.75) 123 | visualisation.add_subplot(plot_df['21ema'], panel=0, color='turquoise', width=0.75) 124 | 125 | # create final plot with title 126 | visualisation.plot_graph("EMA with MACD and RSI strategy") 127 | -------------------------------------------------------------------------------- /trading_strategies/ema_mi.py: -------------------------------------------------------------------------------- 1 | # https://www.incrediblecharts.com/indicators/mass_index.php 2 | 3 | import pandas as pd 4 | import ta 5 | import trading_strategies.visualise as v 6 | 7 | class EMAMI: 8 | def __init__(self, file_path): 9 | #self.df = pd.DataFrame(file_path) 10 | self.df = pd.read_csv(file_path) 11 | self.high = self.df['high'] 12 | self.low = self.df['low'] 13 | self.close = self.df['close'] 14 | 15 | # calculates the EMA (ema) indicator using ta library, period of 9 16 | def calculate_ema(self): 17 | self.df['ema'] = self.df['close'].ewm(span=9, adjust=False).mean() 18 | 19 | # calculates the mass index (mi) using ta library 20 | def calculate_mi(self): 21 | self.df['mi'] = ta.trend.MassIndex(high = self.high, low = self.low).mass_index() # n is left at the default of 9 and n2 is left at the default of 25 22 | 23 | def determine_signal(self, dframe): 24 | # initialise all signals to hold: 0 25 | signal = 0 26 | 27 | # BUY SIGNAL: when mi drops below 26.5 and ema is negatively sloped 28 | if dframe['mi'].iloc[-1] < 26.5 and dframe['mi'].iloc[-2] >= 26.5 and dframe['ema'].iloc[-1] < dframe['ema'].iloc[-2]: 29 | signal = 1 30 | # SELL SIGNAL: when mi drops below 26.5 and ema is positively sloped 31 | elif dframe['mi'].iloc[-1] < 26.5 and dframe['mi'].iloc[-2] >= 26.5 and dframe['ema'].iloc[-1] > dframe['ema'].iloc[-2]: 32 | signal = -1 33 | 34 | return (signal, dframe['ema'].iloc[-1]) 35 | 36 | def run(self): 37 | self.calculate_ema() 38 | self.calculate_mi() 39 | signal = self.determine_signal(self.df) 40 | return signal, self.df 41 | 42 | ''' The following methods are for plotting ''' 43 | 44 | def find_all_signals(self, plot_df): 45 | # assign intitial value of hold 46 | plot_df['signal'] = 0 47 | 48 | start = -1 * len( 49 | plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 50 | end = start + 42 # where the current window will stop (exclusive of the element at this index) 51 | 52 | # loop through data to determine all signals 53 | while end < 0: 54 | curr_window = plot_df[start:end] 55 | action = self.determine_signal(curr_window)[0] 56 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 57 | additional_info = self.determine_signal(curr_window)[1] 58 | plot_df.loc[plot_df.index[end - 1], 'additional_info'] = additional_info 59 | end += 1 60 | start += 1 61 | 62 | # compute final signal 63 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-42:])[0] 64 | 65 | def plot_graph(self): 66 | 67 | # deep copy of data so original is not impacted 68 | plot_df = self.df.copy(deep=True) 69 | 70 | # determine all signals for the dataset 71 | self.find_all_signals(plot_df) 72 | 73 | plot_df['zero_line'] = 0 74 | plot_df['mi_cross_line'] = 26.5 75 | 76 | # initialise visualisation object for plotting 77 | visualisation = v.Visualise(plot_df) 78 | 79 | # determining one buy signal example for plotting 80 | visualisation.determine_buy_marker() 81 | 82 | # determining one sell signal example for plotting 83 | visualisation.determine_sell_marker() 84 | 85 | # add subplots of senkou span A and B to form the ichimoku cloud, and the parabolic sar dots 86 | visualisation.add_subplot(plot_df['ema'], color='orange', width=0.75) 87 | visualisation.add_subplot(plot_df['mi'], panel=1, color='m', width=0.75, ylabel='MI') 88 | visualisation.add_subplot(plot_df['mi_cross_line'], panel=1, color='k', secondary_y=False, width=0.75, linestyle='solid') 89 | 90 | # create final plot with title 91 | visualisation.plot_graph("EMA MI Strategy") 92 | -------------------------------------------------------------------------------- /trading_strategies/force_index_ema.py: -------------------------------------------------------------------------------- 1 | #https://www.daytrading.com/force-index 2 | 3 | import pandas as pd 4 | import mplfinance as mpf 5 | import ta 6 | import numpy as np 7 | 8 | class ForceIndexEMA: 9 | def __init__(self, file_path): 10 | self.df = pd.read_csv(file_path) 11 | self.close = self.df['close'] 12 | self.volume = self.df['volume'] 13 | 14 | # calculates the force index (fi) using ta library 15 | def calculate_force_index(self): 16 | self.df['fi'] = ta.volume.ForceIndexIndicator(close = self.close, volume = self.volume).force_index() # n is left at the default of 13 17 | 18 | # calculates the ema (ema) using ta library 19 | def calculate_ema(self): 20 | self.df['ema'] = ta.trend.EMAIndicator(close=self.close).ema_indicator() # n is left at the default of 14 21 | 22 | def determine_signal(self): 23 | 24 | # initialise all signals to hold: 0 25 | self.df['signal'] = 0 26 | 27 | # BUY SIGNAL: when fi is above 0 and is increasing and ema is positively sloped 28 | self.df.loc[(self.df['fi'] > 0) & (self.df['fi'] > self.df['fi'].shift(1)) & (self.df['ema'] > self.df['ema'].shift(1)), 'signal'] = 1 29 | 30 | # SELL SIGNAL: when fi is below 0 and is decreasing and ema is negatively sloped 31 | self.df.loc[(self.df['fi'] < 0) & (self.df['fi'] < self.df['fi'].shift(1)) & (self.df['ema'] < self.df['ema'].shift(1)), 'signal'] = -1 32 | 33 | # return final data point's signal 34 | return (self.df['signal'].iloc[-1]) 35 | 36 | def run_force_index(self): 37 | self.calculate_force_index() 38 | self.calculate_ema() 39 | 40 | signal = self.determine_signal() 41 | return (signal, self.df['fi'].iloc[-1]) -------------------------------------------------------------------------------- /trading_strategies/kama.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import trading_strategies.visualise as v 4 | ''' 5 | ### Author: Wilson ### 6 | Strategy from: 7 | https://corporatefinanceinstitute.com/resources/knowledge/trading-investing/kaufmans-adaptive-moving-average-kama/ 8 | 9 | This strategy uses the KAMA indicator to analyze the behaviour of the market and predict future price movement 10 | 11 | ''' 12 | 13 | class KAMA: 14 | 15 | def __init__(self, file_path): 16 | #self.df = pd.DataFrame(file_path) 17 | self.df = pd.read_csv(file_path) 18 | 19 | def add_kama(self): 20 | self.df['kama'] = ta.momentum.KAMAIndicator(close = self.df['close'], n = 10, pow1 = 2, pow2 = 30).kama() 21 | 22 | # determine and signal for particular index 23 | def determine_signal(self, dframe): 24 | 25 | signal = 0 26 | 27 | # BUY if price crosses above KAMA 28 | if((dframe['close'].iloc[-2] < dframe['kama'].iloc[-2]) & (dframe['close'].iloc[-1] > dframe['kama'].iloc[-1])): 29 | signal = 1 30 | 31 | #SELL if price crosses below KAMA 32 | if((dframe['close'].iloc[-2] > dframe['kama'].iloc[-2]) & (dframe['close'].iloc[-1] < dframe['kama'].iloc[-1])): 33 | signal = -1 34 | 35 | return signal 36 | 37 | # determine and return additional useful information (kama value at signal point) 38 | def determine_additional_info(self, dframe): 39 | 40 | return dframe.iloc[-1]['kama'] 41 | 42 | 43 | def run_kama(self): 44 | 45 | self.add_kama() 46 | 47 | signal = self.determine_signal(self.df) 48 | additional_info = self.determine_additional_info(self.df) 49 | 50 | result = [] 51 | result.append(signal) 52 | result.append(additional_info) 53 | 54 | return tuple(result), self.df 55 | 56 | def find_all_signals(self, plot_df): 57 | # assign intitial value of hold 58 | plot_df['signal'] = 0 59 | 60 | start = -1*len(plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 61 | end = start + 200 # where the current window will stop (exclusive of the element at this index) 62 | 63 | # loop through data to determine all signals 64 | while end < 0: 65 | curr_window = plot_df[start:end] 66 | action = self.determine_signal(curr_window) 67 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 68 | end += 1 69 | start += 1 70 | 71 | # compute final signal 72 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-200:]) 73 | 74 | def plot_graph(self): 75 | 76 | # deep copy of data so original is not impacted 77 | plot_df = self.df.copy(deep=True) 78 | 79 | # determine all signals for the dataset 80 | self.find_all_signals(plot_df) 81 | 82 | # initialise visualisation object for plotting 83 | visualisation = v.Visualise(plot_df) 84 | 85 | # determining one buy signal example for plotting 86 | visualisation.determine_buy_marker() 87 | 88 | # determining one sell signal example for plotting 89 | visualisation.determine_sell_marker() 90 | 91 | # add subplots of KAMA 92 | visualisation.add_subplot(plot_df['kama'], color="orange") 93 | 94 | # create final plot with title 95 | visualisation.plot_graph("KAMA Strategy") 96 | -------------------------------------------------------------------------------- /trading_strategies/kama_crossover.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import trading_strategies.visualise as v 4 | 5 | # Source: https://corporatefinanceinstitute.com/resources/knowledge/trading-investing/kaufmans-adaptive-moving-average-kama/ 6 | 7 | 8 | 9 | class KAMACrossover: 10 | 11 | def __init__(self, file_path): 12 | #self.df = pd.DataFrame(file_path) 13 | self.df = pd.read_csv(file_path) 14 | 15 | def add_kama_fast(self): 16 | self.df['kama_fast'] = ta.momentum.KAMAIndicator(close = self.df['close'], n = 10, pow1 = 2, pow2 = 30).kama() 17 | 18 | def add_kama_slow(self): 19 | self.df['kama_slow'] = ta.momentum.KAMAIndicator(close = self.df['close'], n = 10, pow1 = 5, pow2 = 30).kama() 20 | 21 | # determine and signal for particular index 22 | def determine_signal(self, dframe): 23 | 24 | signal = 0 25 | 26 | # BUY if Kama fast crosses above kama slow 27 | if dframe['kama_fast'].iloc[-1] > dframe['kama_slow'].iloc[-1] and dframe['kama_fast'].iloc[-2] <= dframe['kama_slow'].iloc[-2]: 28 | signal = 1 29 | # SELL if Kama fast crosses below kama slow 30 | elif dframe['kama_fast'].iloc[-1] < dframe['kama_slow'].iloc[-1] and dframe['kama_fast'].iloc[-2] >= dframe['kama_slow'].iloc[-2]: 31 | signal = -1 32 | 33 | return (signal, dframe['close'].iloc[-1]- dframe['kama_fast'].iloc[-1]) 34 | 35 | 36 | def run(self): 37 | 38 | self.add_kama_fast() 39 | self.add_kama_slow() 40 | signal = self.determine_signal(self.df) 41 | return signal, self.df 42 | 43 | ''' 44 | The following methods are for plotting. 45 | ''' 46 | 47 | def find_all_signals(self, plot_df): 48 | # assign intitial value of hold 49 | plot_df['signal'] = 0 50 | 51 | start = -1 * len( 52 | plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 53 | end = start + 31 # where the current window will stop (exclusive of the element at this index) 54 | 55 | # loop through data to determine all signals 56 | while end < 0: 57 | curr_window = plot_df[start:end] 58 | action = self.determine_signal(curr_window)[0] 59 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 60 | additional_info = self.determine_signal(curr_window)[1] 61 | plot_df.loc[plot_df.index[end - 1], 'additional_info'] = additional_info 62 | end += 1 63 | start += 1 64 | 65 | # compute final signal 66 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-31:])[0] 67 | 68 | def plot_graph(self): 69 | 70 | # deep copy of data so original is not impacted 71 | plot_df = self.df.copy(deep=True) 72 | 73 | # determine all signals for the dataset 74 | self.find_all_signals(plot_df) 75 | 76 | # zero line series for horizontal axis at value 0 in bull power and bear power 77 | plot_df['zero_line'] = 0 78 | 79 | # initialise visualisation object for plotting 80 | visualisation = v.Visualise(plot_df) 81 | 82 | # determining one buy signal example for plotting 83 | visualisation.determine_buy_marker() 84 | 85 | # determining one sell signal example for plotting 86 | visualisation.determine_sell_marker() 87 | 88 | # add subplots of senkou span A and B to form the ichimoku cloud, and the parabolic sar dots 89 | visualisation.add_subplot(plot_df['kama_fast'], color="red") 90 | visualisation.add_subplot(plot_df['kama_slow'], color="blue") 91 | 92 | # create final plot with title 93 | visualisation.plot_graph("KAMA Crossover Strategy") 94 | -------------------------------------------------------------------------------- /trading_strategies/keltner_adx.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import trading_strategies.visualise as v 4 | import numpy as np 5 | 6 | ''' 7 | @author: Caitlin 8 | 9 | 10 | This strategy combines Keltner Channels with ADX. Buy signals are given when at least 3 candles 11 | are at or below the low band, and oversold conditions are confirmed by an adx reading of at least 25. 12 | Sell signals are given when at least 3 candles are at or above the high band, and overbought conditions are 13 | confirmed by an adx reading of at least 20. 14 | ''' 15 | 16 | 17 | class KeltnerAdx: 18 | 19 | def __init__(self, file_path): 20 | #self.max_window = 150 #uncomment for graphing purposes 21 | self.df = pd.read_csv(file_path) 22 | self.high = self.df['high'] 23 | self.low = self.df['low'] 24 | self.close = self.df['close'] 25 | 26 | def calculate_band_upper(self): 27 | band_up_ind = ta.volatility.KeltnerChannel(high=self.high, low=self.low, close=self.close, n=20) 28 | self.df['k_band_upper'] = band_up_ind.keltner_channel_hband() 29 | 30 | def calculate_band_lower(self): 31 | band_low_ind = ta.volatility.KeltnerChannel(high=self.high, low=self.low, close=self.close, n=20) 32 | self.df['k_band_lower'] = band_low_ind.keltner_channel_lband() 33 | 34 | def calculate_band_width(self): 35 | band_width = ta.volatility.KeltnerChannel(high = self.high, low = self.low, close = self.close, n=20) 36 | self.df['band_width'] = band_width.keltner_channel_wband() 37 | 38 | def calculate_adx(self): 39 | adx_ind = ta.trend.ADXIndicator(high=self.high, low=self.low, close=self.close, n=20) 40 | self.df['adx'] = adx_ind.adx() 41 | 42 | def determine_signal(self, dframe): 43 | 44 | # initialise all signals to hold: 0 45 | dframe['signal'] = 0 46 | 47 | # BUY SIGNAL: adx is >= 25 and at least 3 candles are less than or touch the lower keltner band 48 | dframe.loc[((dframe['high'] <= dframe['k_band_lower']) 49 | & (dframe['high'].shift(1) <= dframe['k_band_lower'].shift(1)) 50 | & (dframe['high'].shift(2) <= dframe['k_band_lower'].shift(2)) & (dframe['adx'] >= 20) 51 | ), 'signal'] = 1 52 | 53 | # SELL SIGNAL: adx is >=25 and at least 3 candles are greater than or touch the upper keltner band 54 | dframe.loc[((dframe['low'] >= dframe['k_band_upper']) 55 | & (dframe['low'].shift(1) >= dframe['k_band_upper'].shift(1)) 56 | & (dframe['low'].shift(2) >= dframe['k_band_upper'].shift(2)) & (dframe['adx'] >= 20) 57 | ), 'signal'] = -1 58 | 59 | # return final data point's signal and bandwidth 60 | signal_col = dframe.columns.get_loc('signal') 61 | return (dframe.iloc[-1, signal_col], dframe['band_width'].iloc[-1]) 62 | 63 | def run_keltner_adx(self): 64 | 65 | self.calculate_band_lower() 66 | self.calculate_band_upper() 67 | self.calculate_adx() 68 | self.calculate_band_width() 69 | signal = self.determine_signal(self.df) 70 | return signal, self.df 71 | 72 | ''' The following methods are for plotting ''' 73 | 74 | def find_all_signals(self, plot_df): 75 | # assign intitial value of hold 76 | plot_df['signal'] = 0 77 | 78 | start = -1 * len( 79 | plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 80 | end = start + 150 # where the current window will stop (exclusive of the element at this index) 81 | 82 | # loop through data to determine all signals 83 | while end < 0: 84 | curr_window = plot_df[start:end] 85 | action = self.determine_signal(curr_window)[0] 86 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 87 | end += 1 88 | start += 1 89 | 90 | # compute final signal 91 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-150:])[0] 92 | 93 | def plot_graph(self): 94 | # deep copy of data so original is not impacted 95 | plot_df = self.df.copy(deep=True) 96 | 97 | # determine all signals for the dataset 98 | self.find_all_signals(plot_df) 99 | 100 | # initialise visualisation object for plotting 101 | visualisation = v.Visualise(plot_df) 102 | 103 | # determining one buy signal example for plotting 104 | visualisation.determine_buy_marker() 105 | 106 | # determining one sell signal example for plotting 107 | visualisation.determine_sell_marker() 108 | 109 | line_20 = [20] * self.max_window 110 | 111 | # add subplots of 200ema and awesome oscillator 112 | visualisation.add_subplot(line_20, panel=1, color="orange") 113 | visualisation.add_subplot(plot_df['adx'], panel=1, ylabel="Adx") 114 | visualisation.add_subplot(plot_df['k_band_upper'], color="blue") 115 | visualisation.add_subplot(plot_df['k_band_lower'], color="pink") 116 | 117 | # create final plot with title 118 | visualisation.plot_graph("Keltner Adx Trading Strategy") -------------------------------------------------------------------------------- /trading_strategies/keltner_rsi.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import mplfinance as mpf 4 | import numpy as np 5 | 6 | ''' 7 | @author: Caitlin 8 | 9 | This strategy uses the keltner channel and rsi2 indicator to determine when to place buy and sell orders 10 | The combination with the rsi2 aims to filter out false signals, where the rsi2 shows whether conditions 11 | are overbought or oversold, and candlesticks rising out of the top channel indicating a sell signal and 12 | dipping below the lower channel indicate a buy signal 13 | ''' 14 | 15 | class KeltnerRsi: 16 | def __init__(self, file_path): 17 | #self.max_window = 50 # uncomment for graphing purposes 18 | self.df = pd.read_csv(file_path) 19 | self.high = self.df['high'] 20 | self.close = self.df['close'] 21 | self.low = self.df['low'] 22 | 23 | def calculate_band_upper(self): 24 | band_up_ind = ta.volatility.KeltnerChannel(high=self.high, low=self.low, close=self.close, n=20) 25 | self.df['k_band_upper'] = band_up_ind.keltner_channel_hband() 26 | 27 | def calculate_band_lower(self): 28 | band_low_ind = ta.volatility.KeltnerChannel(high=self.high, low=self.low, close=self.close, n=20) 29 | self.df['k_band_lower'] = band_low_ind.keltner_channel_lband() 30 | 31 | def calculate_rsi2(self): 32 | rsi2_ind = ta.momentum.RSIIndicator(close=self.close, n=2) 33 | self.df['rsi2'] = rsi2_ind.rsi() 34 | 35 | def determine_signal(self, dframe): 36 | 37 | # initialise all signals to hold: 0 38 | dframe['signal'] = 0 39 | 40 | # BUY SIGNAL: rsi2 displays oversold conditions ie less than 10 and at least 3 candlesticks are below the lower band 41 | dframe.loc[(((dframe['rsi2'] < 50) | (dframe['rsi2'].shift(1) < 50) | (dframe['rsi2'].shift(2) < 50) | (dframe['rsi2'].shift(3) < 50)) 42 | & ((dframe['close'] <= dframe['k_band_lower']) | (dframe['close'].shift(1) <= dframe['k_band_lower'].shift(1)) 43 | | (dframe['close'].shift(2) <= dframe['k_band_lower'].shift(2))| (dframe['close'].shift(3) <= dframe['k_band_lower'].shift(3)))), 'signal'] = 1 44 | 45 | # SELL SIGNAL: rsi2 displays overbought conditions ie greater than 90 and at least 3 candlesticks are above the upper band 46 | dframe.loc[(((dframe['rsi2'] > 50) | (dframe['rsi2'].shift(1) > 50) | (dframe['rsi2'].shift(2) > 50) | (dframe['rsi2'].shift(3) > 50)) 47 | & ((dframe['close'] >= dframe['k_band_upper']) | (dframe['close'].shift(1) >= dframe['k_band_upper'].shift(1)) 48 | | (dframe['close'].shift(2) >= dframe['k_band_upper'].shift(2)) | (dframe['close'].shift(3) >= dframe['k_band_upper'].shift(3)))), 'signal'] = -1 49 | 50 | # return final data point's signal and value of rsi2 51 | signal_col = dframe.columns.get_loc('signal') 52 | return(dframe.iloc[-1, signal_col], dframe['rsi2'].iloc[-1]) 53 | 54 | def run_keltner_rsi2(self): 55 | 56 | self.calculate_band_lower() 57 | self.calculate_band_upper() 58 | self.calculate_rsi2() 59 | signal = self.determine_signal(self.df) 60 | return signal, self.df 61 | 62 | ''' The following methods are for plotting ''' 63 | 64 | def find_all_signals(self, plot_df): 65 | # assign intitial value of hold 66 | plot_df['signal'] = 0 67 | 68 | start = -1 * len( 69 | plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 70 | end = start + 50 # where the current window will stop (exclusive of the element at this index) 71 | 72 | # loop through data to determine all signals 73 | while end < 0: 74 | curr_window = plot_df[start:end] 75 | action = self.determine_signal(curr_window)[0] 76 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 77 | end += 1 78 | start += 1 79 | 80 | # compute final signal 81 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-50:])[0] 82 | 83 | def plot_graph(self): 84 | # deep copy of data so original is not impacted 85 | plot_df = self.df.copy(deep=True) 86 | 87 | # determine all signals for the dataset 88 | self.find_all_signals(plot_df) 89 | 90 | # initialise visualisation object for plotting 91 | visualisation = v.Visualise(plot_df) 92 | 93 | # determining one buy signal example for plotting 94 | visualisation.determine_buy_marker() 95 | 96 | # determining one sell signal example for plotting 97 | visualisation.determine_sell_marker() 98 | 99 | line_30 = [30] * self.max_window 100 | line_70 = [70] * self.max_window 101 | 102 | # add subplots of 200ema and awesome oscillator 103 | visualisation.add_subplot(plot_df['rsi2'], panel=1, color="orange") 104 | visualisation.add_subplot(line_30, panel=1, color="blue") 105 | visualisation.add_subplot(line_70, panel=1, color="green") 106 | visualisation.add_subplot(plot_df['k_band_upper']) 107 | visualisation.add_subplot(plot_df['k_band_lower']) 108 | 109 | # create final plot with title 110 | visualisation.plot_graph("Keltner RSI Trading Strategy") -------------------------------------------------------------------------------- /trading_strategies/keltner_stochastic.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import trading_strategies.visualise as v 4 | 5 | 6 | ''' 7 | @author: Caitlin 8 | This strategy combines the keltner channels, with the stochastic signal line. 9 | 10 | ''' 11 | 12 | 13 | class KeltnerStochastic: 14 | 15 | def __init__(self, file_path): 16 | #self.max_window = 100 #uncomment for graphing purposes 17 | self.df = pd.read_csv(file_path) 18 | self.high = self.df['high'] 19 | self.close = self.df['close'] 20 | self.low = self.df['low'] 21 | 22 | def calculate_band_upper(self): 23 | band_up_ind = ta.volatility.KeltnerChannel(high=self.high, low=self.low, close=self.close, n=20) 24 | self.df['k_band_upper'] = band_up_ind.keltner_channel_hband() 25 | 26 | def calculate_band_lower(self): 27 | band_low_ind = ta.volatility.KeltnerChannel(high=self.high, low=self.low, close=self.close, n=20) 28 | self.df['k_band_lower'] = band_low_ind.keltner_channel_lband() 29 | 30 | def calculate_stochastic_signal_line(self): 31 | stoch_signal = ta.momentum.StochasticOscillator(high=self.high, low=self.low, close=self.close) 32 | self.df['stoch_signal'] = stoch_signal.stoch_signal() 33 | 34 | 35 | def determine_signal(self, dataframe): 36 | 37 | # initialise all signals to hold: 0 38 | action = 0 39 | 40 | # BUY SIGNAL: candle close is below lower keltner band, stochastic signal is <=30, psar is below the candle 41 | if dataframe['high'].iloc[-1] < dataframe['k_band_lower'].iloc[-1] and dataframe['stoch_signal'].iloc[-1] < 30: 42 | action = 1 43 | 44 | # SELL SIGNAL: candle close above upper keltner band, stochastic signal >= 70, psar below candle 45 | elif dataframe['low'].iloc[-1] > dataframe['k_band_upper'].iloc[-1] and dataframe['stoch_signal'].iloc[-1] > 70: 46 | action = -1 47 | 48 | 49 | extra = dataframe['stoch_signal'].iloc[-1] 50 | return (action, extra) 51 | 52 | def run_keltner_stochastic(self): 53 | 54 | self.calculate_band_lower() 55 | self.calculate_band_upper() 56 | self.calculate_stochastic_signal_line() 57 | signal = self.determine_signal(self.df) 58 | return signal, self.df 59 | 60 | ''' The following methods are for plotting ''' 61 | 62 | def find_all_signals(self, plot_df): 63 | # assign initial value of hold 64 | plot_df['signal'] = 0 65 | 66 | start = -1 * len( 67 | plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 68 | end = start + 100 # where the current window will stop (exclusive of the element at this index) 69 | 70 | # loop through data to determine all signals 71 | while end < 0: 72 | curr_window = plot_df[start:end] 73 | action = self.determine_signal(curr_window)[0] 74 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 75 | end += 1 76 | start += 1 77 | 78 | # compute final signal 79 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-100:])[0] 80 | 81 | def plot_graph(self): 82 | # deep copy of data so original is not impacted 83 | plot_df = self.df.copy(deep=True) 84 | 85 | # determine all signals for the dataset 86 | self.find_all_signals(plot_df) 87 | 88 | # initialise visualisation object for plotting 89 | visualisation = v.Visualise(plot_df) 90 | 91 | # determining one buy signal example for plotting 92 | visualisation.determine_buy_marker() 93 | 94 | # determining one sell signal example for plotting 95 | visualisation.determine_sell_marker() 96 | 97 | line_30 = [30] * self.max_window 98 | line_70 = [70] * self.max_window 99 | 100 | visualisation.add_subplot(plot_df['stoch_signal'], panel=1, color="blue") 101 | visualisation.add_subplot(line_70, panel=1, color="green") 102 | visualisation.add_subplot(line_30, panel=1, color="black") 103 | visualisation.add_subplot(plot_df['k_band_upper']) 104 | visualisation.add_subplot(plot_df['k_band_lower']) 105 | 106 | # create final plot with title 107 | visualisation.plot_graph("Keltner Stochastic Trading Strategy") -------------------------------------------------------------------------------- /trading_strategies/macd_crossover.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import trading_strategies.visualise as v 4 | 5 | ''' 6 | @ Vita 7 | https://www.dailyfx.com/forex/education/trading_tips/daily_trading_lesson/2020/01/09/macd-histogram.html 8 | 9 | ''' 10 | 11 | 12 | class MACDCrossover: 13 | 14 | def __init__(self, file_path): 15 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos")) 16 | 17 | def add_macd_line(self): 18 | self.df['macd_line'] = ta.trend.MACD(close=self.df['close']).macd() 19 | 20 | def add_macd_signal_line(self): 21 | self.df['macd_signal'] = ta.trend.MACD(close=self.df['close']).macd_signal() 22 | 23 | def determine_signal(self,dframe): 24 | # sell = -1, hold = 0, buy = 1, initialise all as hold first 25 | 26 | action = 0 27 | macd = dframe['macd_line'] 28 | signal = dframe['macd_signal'] 29 | 30 | 31 | # SELL CRITERIA: if MACD line has crossed signal line and are > 0 32 | if (macd.iloc[-1] > 0 and signal.iloc[-1] > 0 and macd.iloc[-2] > 0 and signal.iloc[-2] > 0 and macd.iloc[-3]>0 and signal.iloc[-3]>0) and \ 33 | ((macd.iloc[-3] < signal.iloc[-3] and macd.iloc[-1] > signal.iloc[-1]) or (macd.iloc[-3] > signal.iloc[-3] and macd.iloc[-1] < signal.iloc[-1])): 34 | action = -1 35 | 36 | # BUY CRITERIA: if MACD line has crossed signal line and are < 0 37 | if (macd.iloc[-1] < 0 and signal.iloc[-1] < 0 and macd.iloc[-2] < 0 and signal.iloc[-2] < 0 and 38 | macd.iloc[-3] < 0 and signal.iloc[-3] < 0) and \ 39 | ((macd.iloc[-3] > signal.iloc[-3] and macd.iloc[-1] < signal.iloc[-1]) or ( 40 | macd.iloc[-3] < signal.iloc[-3] and macd.iloc[-1] > signal.iloc[-1])): 41 | action = 1 42 | 43 | 44 | return action, macd.iloc[-1]-signal.iloc[-1] 45 | 46 | def run_macd_crossover(self): 47 | self.add_macd_line() 48 | self.add_macd_signal_line() 49 | return self.determine_signal(self.df), self.df 50 | 51 | ''' The following methods are for plotting ''' 52 | def find_all_signals(self, plot_df): 53 | # assign initial value of hold 54 | plot_df['signal'] = 0 55 | 56 | start = -1 * len(plot_df) 57 | end = start + 37 58 | 59 | # loop through data to determine all signals 60 | while end < 0: 61 | curr_window = plot_df[start:end] 62 | action = self.determine_signal(curr_window)[0] 63 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 64 | end += 1 65 | start += 1 66 | 67 | action = self.determine_signal(plot_df[-37:])[0] 68 | plot_df.loc[plot_df.index[-1], 'signal'] = action 69 | 70 | 71 | def plot_graph(self): 72 | # create shallow copy for plotting so as to not accidentally impact original df 73 | plot_df = self.df.copy(deep=False) 74 | self.find_all_signals(plot_df) 75 | 76 | # initialise visualisation object for plotting 77 | visualisation = v.Visualise(plot_df) 78 | 79 | # determining one buy signal example for plotting 80 | visualisation.determine_buy_marker() 81 | 82 | # determining one sell signal example for plotting 83 | visualisation.determine_sell_marker() 84 | 85 | # add subplots 86 | visualisation.add_subplot(plot_df['macd_line'], panel=1, color='pink', width=0.75, ylabel='MACD line\n(pink)') 87 | visualisation.add_subplot(plot_df['macd_signal'], panel=1, color='b', width=0.75, ylabel='MACD signal\n(blue)') 88 | 89 | # create final plot with title 90 | visualisation.plot_graph("MACD Crossover Strategy") 91 | 92 | 93 | -------------------------------------------------------------------------------- /trading_strategies/macd_histogram_reversal.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | import mplfinance as mpf 5 | import ta 6 | import trading_strategies.visualise as v 7 | 8 | ''' 9 | ### Author: Wilson ### 10 | Strategy from: 11 | https://www.ig.com/au/trading-strategies/macd-trading-strategy-190610#histogramreversals 12 | ''' 13 | 14 | class MACDHistogramReversal: 15 | 16 | def __init__(self, file_path): 17 | self.df = pd.read_csv(file_path) 18 | #self.df = pd.DataFrame(file_path) 19 | 20 | def add_macd_line(self): 21 | self.df['macd_line'] = ta.trend.MACD(close = self.df['close']).macd() 22 | 23 | def add_macd_signal_line(self): 24 | self.df['macd_signal'] = ta.trend.MACD(close = self.df['close']).macd_signal() 25 | 26 | def add_macd_diff(self): 27 | self.df['macd_histogram'] = ta.trend.MACD(close = self.df['close']).macd_diff() 28 | 29 | # determine and signal for particular index 30 | def determine_signal(self, dframe): 31 | 32 | signal = 0 33 | 34 | #BUY MACD histogram is reversing up 35 | if((dframe['macd_histogram'].iloc[-1] > dframe['macd_histogram'].iloc[-2]) & 36 | (dframe['macd_histogram'].iloc[-2] > dframe['macd_histogram'].iloc[-3]) & 37 | (dframe['macd_histogram'].iloc[-3] < dframe['macd_histogram'].iloc[-4]) & 38 | (dframe['macd_histogram'].iloc[-4] < dframe['macd_histogram'].iloc[-5])): 39 | 40 | signal = 1 41 | 42 | #SELL if MACD histogram is reversing down 43 | if((dframe['macd_histogram'].iloc[-1] < dframe['macd_histogram'].iloc[-2]) & 44 | (dframe['macd_histogram'].iloc[-2] < dframe['macd_histogram'].iloc[-3]) & 45 | (dframe['macd_histogram'].iloc[-3] > dframe['macd_histogram'].iloc[-4]) & 46 | (dframe['macd_histogram'].iloc[-4] > dframe['macd_histogram'].iloc[-5])): 47 | 48 | signal = -1 49 | 50 | return signal 51 | 52 | # determine and return additional useful information 53 | def determine_additional_info(self, dframe): 54 | 55 | return dframe.iloc[-1]['macd_histogram'] - dframe.iloc[-1]['close'] 56 | 57 | def run_macd_histogram_reversal(self): 58 | 59 | # perform calculations 60 | self.add_macd_line() 61 | self.add_macd_signal_line() 62 | self.add_macd_diff() 63 | 64 | # generate data for return tuple 65 | signal = self.determine_signal(self.df) 66 | additional_info = self.determine_additional_info(self.df) 67 | 68 | #create return tuple and append data 69 | result = [] 70 | result.append(signal) 71 | result.append(additional_info) 72 | 73 | return tuple(result), self.df 74 | 75 | ''' The following methods are for plotting ''' 76 | def find_all_signals(self, plot_df): 77 | # assign initial value of hold 78 | plot_df['signal'] = 0 79 | 80 | start = -1 * len(plot_df) 81 | end = start + 37 82 | 83 | # loop through data to determine all signals 84 | while end < 0: 85 | curr_window = plot_df[start:end] 86 | action = self.determine_signal(curr_window) 87 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 88 | end += 1 89 | start += 1 90 | 91 | action = self.determine_signal(plot_df[-37:]) 92 | plot_df.loc[plot_df.index[-1], 'signal'] = action 93 | 94 | 95 | def plot_graph(self): 96 | # create shallow copy for plotting so as to not accidentally impact original df 97 | plot_df = self.df.copy(deep=False) 98 | self.find_all_signals(plot_df) 99 | 100 | # initialise visualisation object for plotting 101 | visualisation = v.Visualise(plot_df) 102 | 103 | # determining one buy signal example for plotting 104 | visualisation.determine_buy_marker() 105 | 106 | # determining one sell signal example for plotting 107 | visualisation.determine_sell_marker() 108 | 109 | # add subplots 110 | visualisation.add_subplot(plot_df['macd_line'], panel=1, color='pink', width=0.75, ylabel='MACD line\n(pink)') 111 | visualisation.add_subplot(plot_df['macd_signal'], panel=1, color='b', width=0.75, ylabel='MACD signal\n(blue)') 112 | visualisation.add_subplot(plot_df['macd_histogram'], panel=2, color='r', type = 'bar', width=0.75, ylabel='Histogram\n(red)') 113 | 114 | 115 | # create final plot with title 116 | visualisation.plot_graph("MACD Histogram Reversal Strategy") 117 | -------------------------------------------------------------------------------- /trading_strategies/macd_zero_cross.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | import mplfinance as mpf 5 | import ta 6 | import trading_strategies.visualise as v 7 | 8 | ''' 9 | ### Author: Wilson ### 10 | Strategy from: 11 | https://www.ig.com/au/trading-strategies/macd-trading-strategy-190610#zerocrosses 12 | ''' 13 | 14 | class MACDZeroCross: 15 | 16 | 17 | def __init__(self, file_path): 18 | self.df = pd.read_csv(file_path) 19 | #self.df = pd.DataFrame(file_path) 20 | 21 | def add_macd_line(self): 22 | self.df['macd_line'] = ta.trend.MACD(close = self.df['close']).macd() 23 | 24 | # determine and signal for particular index 25 | def determine_signal(self, dframe): 26 | 27 | signal = 0 28 | 29 | #BUY if macd line crosses above zero 30 | if((dframe['macd_line'].iloc[-2] < 0) & (dframe['macd_line'].iloc[-1] > 0)): 31 | signal = 1 32 | 33 | #SELL if macd line crosses below zero 34 | if((dframe['macd_line'].iloc[-2] > 0) & (dframe['macd_line'].iloc[-1] < 0)): 35 | signal = -1 36 | 37 | return signal 38 | 39 | # determine and return additional useful information 40 | def determine_additional_info(self, dframe): 41 | 42 | return dframe.iloc[-1]['close'] - dframe.iloc[-1]['macd_line'] 43 | 44 | def run_macd_zero_cross(self): 45 | 46 | #perform calculations 47 | self.add_macd_line() 48 | 49 | #generate data for return tuple 50 | signal = self.determine_signal(self.df) 51 | additional_info = self.determine_additional_info(self.df) 52 | 53 | #create return tuple and append data 54 | result = [] 55 | result.append(signal) 56 | result.append(additional_info) 57 | 58 | return tuple(result), self.df 59 | 60 | ''' The following methods are for plotting ''' 61 | def find_all_signals(self, plot_df): 62 | # assign initial value of hold 63 | plot_df['signal'] = 0 64 | 65 | start = -1 * len(plot_df) 66 | end = start + 37 67 | 68 | # loop through data to determine all signals 69 | while end < 0: 70 | curr_window = plot_df[start:end] 71 | action = self.determine_signal(curr_window) 72 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 73 | end += 1 74 | start += 1 75 | 76 | action = self.determine_signal(plot_df[-37:]) 77 | plot_df.loc[plot_df.index[-1], 'signal'] = action 78 | 79 | 80 | def plot_graph(self): 81 | # create shallow copy for plotting so as to not accidentally impact original df 82 | plot_df = self.df.copy(deep=False) 83 | self.find_all_signals(plot_df) 84 | 85 | # initialise visualisation object for plotting 86 | visualisation = v.Visualise(plot_df) 87 | 88 | # determining one buy signal example for plotting 89 | visualisation.determine_buy_marker() 90 | 91 | # determining one sell signal example for plotting 92 | visualisation.determine_sell_marker() 93 | 94 | # add subplots 95 | visualisation.add_subplot(plot_df['macd_line'], panel=1, color='pink', width=0.75, ylabel='MACD line\n(pink)') 96 | 97 | 98 | 99 | # create final plot with title 100 | visualisation.plot_graph("MACD Zero Cross Strategy") 101 | -------------------------------------------------------------------------------- /trading_strategies/mfi.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pandas as pd 4 | import mplfinance as mpf 5 | import ta 6 | import numpy as np 7 | import trading_strategies.visualise as v 8 | 9 | 10 | file_path = r"C:\Uni\2020 Sem 2\INFO3600 Group Work\unit_tests\inputs\200_periods_in.csv" 11 | 12 | class MFI: 13 | def __init__(self, file_path): 14 | #self.df = pd.read_csv(file_path) 15 | self.df = pd.DataFrame(file_path) 16 | self.high = self.df['high'] 17 | self.low = self.df['low'] 18 | self.close = self.df['close'] 19 | self.volume = self.df['vol'] 20 | 21 | def calculate_mfi(self): 22 | self.df['mfi'] = ta.volume.MFIIndicator(high = self.high, low = self.low, close = self.close, volume = self.volume).money_flow_index() # n is left at the default of 14 23 | 24 | def determine_signal(self): 25 | 26 | #initialise all signals to hold: 0 27 | self.df['signal'] = 0 28 | 29 | #BUY SIGNAL: when mfi was in oversold zone of below 20 and crosses above 20, the bearish market is ready to return to normal 30 | self.df.loc[(self.df['mfi'] > 20) & (self.df['mfi'].shift(1) <= 20), 'signal'] = 1 31 | 32 | #SELL SIGNAL: when mfi was in overbought zone of above 80 and crosses below 80, the bullish market is ready to return to normal 33 | self.df.loc[(self.df['mfi'] < 80) & (self.df['mfi'].shift(1) >= 80), 'signal'] = -1 34 | 35 | #return final data point's signal 36 | return(self.df['signal'].iloc[-1]) 37 | 38 | def run(self): 39 | self.calculate_mfi() 40 | signal = self.determine_signal() 41 | return (signal, self.df['mfi'].iloc[-1]) 42 | 43 | ''' The following methods are for plotting ''' 44 | 45 | def determine_buy_marker(self, plot_df): 46 | plot_df['buy_marker'] = np.nan 47 | # get index of first buy example in input dataset 48 | buy_index_ls = plot_df.index[(plot_df['signal'] == 1)].tolist() 49 | # if a buy example exists 50 | if buy_index_ls: 51 | buy_marker_index = buy_index_ls[0] 52 | # set buy marker value to a little lower than other graph elements (for visual purposes) 53 | plot_df.loc[buy_marker_index, 'buy_marker'] = plot_df.loc[buy_marker_index, 'low'] - plot_df.range * 0.1 54 | 55 | def determine_sell_marker(self, plot_df): 56 | plot_df['sell_marker'] = np.nan 57 | # get index of first sell example in input dataset 58 | sell_index_ls = plot_df.index[(plot_df['signal'] == -1)].tolist() 59 | # if sell example exists 60 | if sell_index_ls: 61 | sell_marker_index = sell_index_ls[0] 62 | # set sell marker value to a little higher than other graph elements (for visual purposes) 63 | plot_df.loc[sell_marker_index, 'sell_marker'] = plot_df.loc[ 64 | sell_marker_index, 'high'] + plot_df.range * 0.1 65 | 66 | def plot_graph(self): 67 | # create shallow copy for plotting so as to not accidentally impact original df 68 | plot_df = self.df.copy(deep=False) 69 | plot_df = plot_df 70 | plot_df.range = max(plot_df['high']) - min(plot_df['low']) 71 | # determine buy and/or sell marker 72 | self.determine_buy_marker(plot_df) 73 | self.determine_sell_marker(plot_df) 74 | 75 | # set index to datetime 76 | plot_df.index = pd.to_datetime(plot_df.datetime) 77 | # Create colour style colours to green and red 78 | mc = mpf.make_marketcolors(up='g', down='r') 79 | 80 | line_20 = self.df['signal'].size * [20] 81 | line_80 = self.df['signal'].size * [80] 82 | 83 | apds = [ 84 | mpf.make_addplot((plot_df['mfi']), panel=1, color='m', width=0.75, ylabel='MFI'), 85 | mpf.make_addplot(line_20, panel=1, color='k', secondary_y=False, width=0.75, linestyle='solid'), 86 | mpf.make_addplot(line_80, panel=1, color='k', secondary_y=False, width=0.75, linestyle='solid') 87 | ] 88 | 89 | # if current subset of data has a buy/sell example, add to plot 90 | if not plot_df['buy_marker'].isnull().all(): 91 | apds.append( 92 | mpf.make_addplot(plot_df['buy_marker'], type="scatter", marker="^", markersize=60, color="green")) 93 | if not plot_df['sell_marker'].isnull().all(): 94 | apds.append( 95 | mpf.make_addplot(plot_df['sell_marker'], type="scatter", marker="v", markersize=60, color="red")) 96 | 97 | # Set colour and grid style 98 | s = mpf.make_mpf_style(marketcolors=mc) 99 | mpf.plot(plot_df, type='candle', style=s, addplot=apds, title="\nMFI Strategy") 100 | 101 | #strategy = MFI(file_path) 102 | #signal = strategy.run_mfi() 103 | #print(strategy.plot_graph()) 104 | -------------------------------------------------------------------------------- /trading_strategies/mfi_stochastic.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Sep 27 10:19:23 2020 4 | 5 | @author: mingy 6 | """ 7 | 8 | import pandas as pd 9 | import mplfinance as mpf 10 | import ta 11 | import numpy as np 12 | 13 | class MfiStochastic: 14 | def __init__(self, file_path): 15 | self.df = pd.read_csv(file_path) 16 | self.high = self.df['high'] 17 | self.low = self.df['low'] 18 | self.close = self.df['close'] 19 | self.volume = self.df['volume'] 20 | 21 | def calculate_mfi(self): 22 | self.df['mfi'] = ta.volume.MFIIndicator(high = self.high, low = self.low, close = self.close, volume = self.volume).money_flow_index() #n is left at the default of 14 23 | 24 | def calculate_stochastic(self): 25 | self.df['stoch'] = ta.momentum.StochasticOscillator(high = self.high, low = self.low, close = self.close).stoch() #n is left at the default of 14 26 | 27 | def determine_signal(self): 28 | 29 | #initialise all signals to hold: 0 30 | self.df['signal'] = 0 31 | 32 | #BUY SIGNAL: when mfi and the stochastic indicator both leaves the oversold zone 33 | self.df.loc[(self.df['mfi'] > 20) & (self.df['mfi'].shift(1) <= 20) & (self.df['stoch'] > 20) & (self.df['stoch'].shift(1) <= 20), 'signal'] = 1 34 | 35 | #SELL SIGNAL: when mfi and the stochastic indicator both leaves the overbought zone 36 | self.df.loc[(self.df['mfi'] < 80) & (self.df['mfi'].shift(1) >= 80) & (self.df['stoch'] < 80) & (self.df['stoch'].shift(1) >= 80), 'signal'] = -1 37 | 38 | #return final data point's signal 39 | return(self.df['signal'].iloc[-1]) 40 | 41 | def run_mfi_stochastic(self): 42 | self.calculate_mfi() 43 | self.calculate_stochastic() 44 | signal = self.determine_signal() 45 | return tuple(signal), self.df -------------------------------------------------------------------------------- /trading_strategies/oops_signals.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Sep 7 17:20:00 2020 4 | 5 | @author: mingyu guo 6 | 7 | The oops signal strategy determines at market open if the opening position signals can be taken advantage of with short stop above the opening price or long stop below the opening price 8 | """ 9 | 10 | import datetime 11 | import pandas as pd 12 | import mplfinance as mpf 13 | 14 | class OopsSignals: 15 | 16 | #loading the data in from file_path 17 | def __init__(self, file_path): 18 | self.df = pd.read_csv(file_path, parse_dates=['datetime']) 19 | 20 | # created is_yesterday column to mark all of yesterday's data 21 | def mark_yesterday(self): 22 | current_date = self.df['date'].iloc[-1].to_pydatetime().date() 23 | yesterday_date = current_date - datetime.timedelta(days=1) 24 | self.df.loc[self.df['datetime'].dt.date == yesterday_date, 'is_yesterday'] = 1 25 | 26 | # Calculates the high of yesterday's market 27 | def calculate_yesterday_high(self): 28 | return self.df.loc[self.df['is_yesterday'] == 1, 'high'].max() 29 | 30 | # Calculate the low of yesterday's market 31 | def calculate_yesterday_low(self): 32 | return self.df.loc[self.df['is_yesterday'] == 1, 'low'].min() 33 | 34 | # Find today's opening price 35 | def calculate_open(self): 36 | yesterday_closing_index = self.df.loc[self.df['is_yesterday'] == 1].index.max() 37 | return self.df['open'].iloc[yesterday_closing_index + 1] 38 | 39 | def calculate_current_price(self): 40 | return self.df['close'].iloc[-1] 41 | 42 | def plot_graph(self): 43 | self.df.index = pd.to_datetime(self.df.datetime) 44 | self.df = self.df[-150:] 45 | tcdf = self.df[['bb_low_band','bb_high_band','sma']] #create list of dicts and pass it to the addplot keyword 46 | apds = [ mpf.make_addplot(tcdf), 47 | mpf.make_addplot((self.df['stoch_rsi']), panel = 1, color = 'g') 48 | ] 49 | mpf.plot(self.df, type = 'candle', addplot = apds) 50 | 51 | # Runs the oops signal strategy 52 | def run_oops_signals(self): 53 | self.mark_yesterday() 54 | yesterday_high = self.calculate_yesterday_high() 55 | yesterday_low = self.calculate_yesterday_low() 56 | opening = self.calculate_open() 57 | current_price = self.calculate_current_price() 58 | 59 | if (opening < yesterday_low) & (current_price < opening): 60 | return (1, opening) 61 | elif (opening > yesterday_high) & (current_price > opening): 62 | return (-1, opening) 63 | else: 64 | return (0,None) 65 | 66 | -------------------------------------------------------------------------------- /trading_strategies/preprocessor.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | """import strategies""" 4 | #import 5 | from awesome_saucer import AwesomeOscillatorSaucer 6 | from ichimoku_cloud_psar import IchimokuCloudPsar 7 | 8 | """loading the data set to be used as input for the AI""" 9 | # file path 10 | file_path = 'FXCM_EUR_GBP_H4.csv' 11 | data = pd.read_csv(file_path) 12 | 13 | """setting the vaule of a pip, usually 0.0001 but 0.01 for JPY currencies, and trade differential, the price difference 14 | at which the trade is to be classified as a buy sell or hold.""" 15 | pips = 0.0001 16 | trade_differential = 10*pips 17 | 18 | """loading all the strategies to be used as attributes for the AI""" 19 | # instantiating the stategies list 20 | strategies = [] 21 | 22 | # instantiate and adding all strategies objects into strategies list 23 | strategy_1 = AwesomeOscillatorSaucer(file_path) 24 | strategies.append(strategy_1) 25 | strategy_2 = IchimokuCloudPsar(file_path) 26 | strategies.append(strategy_2) 27 | 28 | """determining the correct signal for use as the class in AI""" 29 | data['correct_signal'] = 0 30 | data.loc[data['close'].shift(-10) - data['close'] > trade_differential, 'correct_signal'] = 1 31 | data.loc[data['close'].shift(-10) - data['close'] > -trade_differential, 'correct_signal'] = -1 32 | 33 | """running the 10 strategies for the action and additional info 34 | for running in a loop, rename the run function to just run() 35 | in find_all_signal() add in lines 36 | additional_info = self.determine_signal(curr_window)[1] 37 | plot_df.loc[plot_df.index[end - 1], 'additional_info'] = additional_info 38 | to get additional info 39 | get rid off the max window if you have that in the code""" 40 | 41 | n = 0 42 | for strategy in strategies: 43 | n = n+1 44 | strategy.run() 45 | strategy.find_all_signals(strategy.df) 46 | data["strategy_" + str(n) + "_action"] = strategy.df['signal'] 47 | data["strategy_" + str(n) + "_additional_info"] = strategy.df['additional_info'] 48 | 49 | """saving to csv""" 50 | data.to_csv('FXCM_EUR_GBP_H4_post.csv') 51 | print("Successfully generated processed file!") -------------------------------------------------------------------------------- /trading_strategies/rsi_2.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import trading_strategies.visualise as v 4 | 5 | ''' 6 | Larry Connors' 2 period RSI strategy uses mean reversion to provide a short-term buy or sell signal. 7 | When the price is above the 200 Moving Average, and 2-period RSI is below 10, this is a buy signal 8 | When the price is below the 200 Moving Average, and 2-period RSI is above 90, this is a sell signal 9 | ''' 10 | 11 | class Rsi2: 12 | def __init__(self, file_path): 13 | self.max_window = 400 # set to 400 for better understanding (can see 200sma through a 200 candlestick period) 14 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos"))[-self.max_window:] 15 | 16 | def calculate_long_ma(self): 17 | # initialise SMA indicator at 200 periods 18 | lma_ind = ta.trend.SMAIndicator(close = self.df['close'], window = 200) 19 | # generate 200 ma 20 | self.df['200sma'] = lma_ind.sma_indicator() 21 | 22 | def calculate_short_ma(self): 23 | # initialise SMA indicator at 5 periods 24 | sma_ind = ta.trend.SMAIndicator(close = self.df['close'], window = 5) 25 | # generate 200 ma 26 | self.df['5sma'] = sma_ind.sma_indicator() 27 | 28 | def calculate_rsi(self): 29 | # initialise RSI indicator 30 | rsi_ind = ta.momentum.RSIIndicator(close = self.df['close'], window = 2) 31 | # generate RSI-2 32 | self.df['rsi2'] = rsi_ind.rsi() 33 | 34 | def determine_signal(self, dframe): 35 | action = 0 36 | # Buy when RSI2 between 0 and 10, and price above 200sma but below 5sma 37 | if dframe['rsi2'].iloc[-1] < 10 and dframe['close'].iloc[-1] > dframe['200sma'].iloc[-1] and dframe['close'].iloc[-1] < dframe['5sma'].iloc[-1]: 38 | action = 1 39 | # Sell when RSI2 between 90 and 100, and price below 200sma but above 5sma 40 | elif dframe['rsi2'].iloc[-1] > 90 and dframe['close'].iloc[-1] < dframe['200sma'].iloc[-1] and dframe['close'].iloc[-1] > dframe['5sma'].iloc[-1]: 41 | action = -1 42 | sma_5_dist = dframe['close'].iloc[-1] - dframe['5sma'].iloc[-1] 43 | sma_200_dist = dframe['close'].iloc[-1] - dframe['200sma'].iloc[-1] 44 | return (action, sma_5_dist, sma_200_dist) 45 | 46 | def run_rsi2(self): 47 | self.calculate_long_ma() 48 | self.calculate_short_ma() 49 | self.calculate_rsi() 50 | signal = self.determine_signal(self.df) 51 | return signal, self.df 52 | 53 | ''' The following methods are for plotting ''' 54 | 55 | def find_all_signals(self, plot_df): 56 | # assign intitial value of hold 57 | plot_df['signal'] = 0 58 | 59 | start = -1*len(plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 60 | end = start + 200 # where the current window will stop (exclusive of the element at this index) 61 | 62 | # loop through data to determine all signals 63 | while end < 0: 64 | curr_window = plot_df[start:end] 65 | action = self.determine_signal(curr_window)[0] 66 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 67 | end += 1 68 | start += 1 69 | 70 | # compute final signal 71 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-200:])[0] 72 | 73 | def plot_graph(self): 74 | 75 | # deep copy of data so original is not impacted 76 | plot_df = self.df.copy(deep=True) 77 | 78 | # determine all signals for the dataset 79 | self.find_all_signals(plot_df) 80 | 81 | # oversold and overbought horizontal lines for easy identification of RSI criteria 82 | plot_df['oversold_line'] = 10 83 | plot_df['overbought_line'] = 90 84 | 85 | # initialise visualisation object for plotting 86 | visualisation = v.Visualise(plot_df) 87 | 88 | # determining one buy signal example for plotting 89 | visualisation.determine_buy_marker() 90 | 91 | # determining one sell signal example for plotting 92 | visualisation.determine_sell_marker() 93 | 94 | # add subplots of 200 SMA, RSI-2, 5 SMA, oversold and overbought lines 95 | visualisation.add_subplot(plot_df['200sma'], color='g') 96 | visualisation.add_subplot(plot_df['rsi2'], panel = 1, ylabel='RSI-2') 97 | visualisation.add_subplot(plot_df['5sma'], color='orange') 98 | visualisation.add_subplot(plot_df['oversold_line'], color='grey', panel = 1, secondary_y = False) 99 | visualisation.add_subplot(plot_df['overbought_line'], color='grey', panel = 1, secondary_y = False) 100 | 101 | # create final plot with title 102 | visualisation.plot_graph("Larry Connors' RSI-2 Strategy") 103 | -------------------------------------------------------------------------------- /trading_strategies/sma_ema.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import trading_strategies.visualise as v 3 | 4 | # @author: vita 5 | 6 | # This strategy uses a 5 day simple movinng average (SMA) for sell and buy signals and a 7 | # 144 period and 169 period exponential moving average (EMA) to determine trend direction 8 | 9 | class SimpleMAExponentialMA: 10 | def __init__(self, file_path): 11 | #self.df = pd.read_csv(file_path) 12 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos")) 13 | self.close = self.df['close'] # retrieves the most recent closing price 14 | 15 | def calculate_144ema(self): 16 | self.df['144ema'] = self.df['close'].ewm(span=144, min_periods=144, adjust=False).mean() 17 | 18 | def calculate_169ema(self): 19 | self.df['169ema'] = self.df['close'].ewm(span=169, min_periods=169, adjust=False).mean() 20 | 21 | def calculate_5sma(self): 22 | self.df['5sma'] = self.df['close'].rolling(window=5).mean() 23 | 24 | def determine_signal(self, dframe): 25 | action = 0 # hold 26 | 27 | close = dframe['close'] 28 | ema_144 = dframe['144ema'] 29 | ema_169 = dframe['169ema'] 30 | sma_5 = dframe['5sma'] 31 | 32 | # SELL CRITERIA: if closing price is below SMA and 169-period EMA is above 144-period EMA 33 | if (close.iloc[-1] < sma_5.iloc[-1]) and (ema_169.iloc[-1] > ema_144.iloc[-1]): 34 | action = -1 35 | # BUY CRITERIA: closing price is above SMA and 144-period EMA is above 169-period EMA 36 | elif (close.iloc[-1] > sma_5.iloc[-1]) and (ema_144.iloc[-1] > ema_169.iloc[-1]): 37 | action = 1 38 | 39 | return action, ema_144.iloc[-1] - ema_169.iloc[-1], 40 | 41 | def run_sma_ema(self): 42 | self.calculate_144ema() 43 | self.calculate_169ema() 44 | self.calculate_5sma() 45 | signal = self.determine_signal(self.df) 46 | 47 | return signal, self.df 48 | 49 | ''' The following methods are for plotting ''' 50 | 51 | def find_all_signals(self, plot_df): 52 | # assign initial value of hold 53 | plot_df['signal'] = 0 54 | 55 | start = -1 * len(plot_df) 56 | end = start + 169 57 | 58 | # loop through data to determine all signals 59 | while end < 0: 60 | curr_window = plot_df[start:end] 61 | action = self.determine_signal(curr_window)[0] 62 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 63 | end += 1 64 | start += 1 65 | 66 | action = self.determine_signal(plot_df[-169:])[0] 67 | plot_df.loc[plot_df.index[-1], 'signal'] = action 68 | 69 | def plot_graph(self): 70 | # create shallow copy for plotting so as to not accidentally impact original df 71 | plot_df = self.df.copy(deep=False) 72 | 73 | self.find_all_signals(plot_df) 74 | 75 | # initialise visualisation object for plotting 76 | visualisation = v.Visualise(plot_df) 77 | 78 | # determining one buy signal example for plotting 79 | visualisation.determine_buy_marker() 80 | 81 | # determining one sell signal example for plotting 82 | visualisation.determine_sell_marker() 83 | 84 | # add subplots 85 | visualisation.add_subplot(plot_df['144ema'], color='turquoise', width=0.75) 86 | visualisation.add_subplot(plot_df['169ema'], color='violet', width=0.75) 87 | visualisation.add_subplot(plot_df['5sma'], color='orange', width=0.75) 88 | 89 | # create final plot with title 90 | visualisation.plot_graph("Simple and Exponential Moving Averages Strategy") 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /trading_strategies/sma_ema_alternative.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import trading_strategies.visualise as v 3 | 4 | # @author: vita 5 | # This strategy uses a 5 day simple movinng average (SMA) for sell and buy signals and a 6 | # 144 period and 169 period exponential moving average (EMA) to determine trend direction 7 | # over a 4 hour time frame 8 | 9 | class SimpleMAExponentialMA: 10 | def __init__(self, inputs): 11 | self.df = pd.read_csv(inputs) 12 | # self.df = pd.DataFrame(inputs) 13 | self.close = self.df['close'] # retrieves the most recent closing price 14 | 15 | def calculate_144ema(self): 16 | self.df['144ema'] = self.df['close'].ewm(span=144, adjust=False).mean() 17 | 18 | def calculate_169ema(self): 19 | self.df['169ema'] = self.df['close'].ewm(span=169, adjust=False).mean() 20 | 21 | def calculate_5sma(self): 22 | self.df['5sma'] = self.df['close'].rolling(window=5).mean() 23 | 24 | def determine_signal(self, dframe): 25 | # sell = -1, hold = 0, buy = 1, intialise all as hold first 26 | action = 0 27 | 28 | ema_144 = self.df['144ema'] 29 | ema_169 = self.df['169ema'] 30 | sma_5 = self.df['5sma'] 31 | 32 | 33 | # SELL CRITERIA: if closing price is below SMA and 169-period EMA is above 144-period EMA 34 | if ((self.close.iloc[-1] < sma_5.iloc[-1]) or (self.close.iloc[-2] < sma_5.iloc[-2])) and (ema_169.iloc[-1] > ema_144.iloc[-1]): 35 | action = -1 36 | # BUY CRITERIA: closing price is below simple moving average and 144-period EMA is above 169-period EMA 37 | elif ((self.close.iloc[-1] > sma_5.iloc[-1]) or (self.close.iloc[-2] > sma_5.iloc[-2])) and (ema_169.iloc[-1] > ema_144.iloc[-1]): 38 | action = 1 39 | 40 | return action, ema_144.iloc[-1] - ema_169.iloc[-1] 41 | 42 | def run_sma_ema(self): 43 | self.calculate_144ema() 44 | self.calculate_169ema() 45 | self.calculate_5sma() 46 | 47 | return self.determine_signal(self.df), self.df 48 | 49 | ''' The following methods are for plotting ''' 50 | ''' The following methods are for plotting ''' 51 | 52 | def find_all_signals(self, plot_df): 53 | # assign initial value of hold 54 | plot_df['signal'] = 0 55 | 56 | start = -1 * len(plot_df) 57 | end = start + 5 58 | 59 | # loop through data to determine all signals 60 | while end < 0: 61 | curr_window = plot_df[start:end] 62 | action = self.determine_signal(curr_window)[0] 63 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 64 | end += 1 65 | start += 1 66 | 67 | action = self.determine_signal(plot_df[-5:])[0] 68 | plot_df.loc[plot_df.index[-1], 'signal'] = action 69 | 70 | def plot_graph(self): 71 | # create shallow copy for plotting so as to not accidentally impact original df 72 | plot_df = self.df.copy(deep=False) 73 | 74 | self.find_all_signals(plot_df) 75 | 76 | # initialise visualisation object for plotting 77 | visualisation = v.Visualise(plot_df) 78 | 79 | # determining one buy signal example for plotting 80 | visualisation.determine_buy_marker() 81 | 82 | # determining one sell signal example for plotting 83 | visualisation.determine_sell_marker() 84 | 85 | # add subplots 86 | visualisation.add_subplot(plot_df['144ema'], color='turquoise', width=0.75) 87 | visualisation.add_subplot(plot_df['169ema'], color='violet', width=0.75) 88 | visualisation.add_subplot(plot_df['5sma'], color='orange', width=0.75) 89 | 90 | # create final plot with title 91 | visualisation.plot_graph("Simple and Exponential Moving Averages Strategy") 92 | 93 | -------------------------------------------------------------------------------- /trading_strategies/sma_mi.py: -------------------------------------------------------------------------------- 1 | # https://www.daytrading.com/mass-index 2 | 3 | import pandas as pd 4 | import ta 5 | import trading_strategies.visualise as v 6 | 7 | 8 | class SMAMI: 9 | def __init__(self, file_path): 10 | #self.df = pd.DataFrame(file_path) 11 | self.df = pd.read_csv(file_path) 12 | self.high = self.df['high'] 13 | self.low = self.df['low'] 14 | self.close = self.df['close'] 15 | 16 | # calculates the fast sma indicator (sma_fast) using ta library, period of 21 17 | def calculate_sma_fast(self): 18 | self.df['sma_fast'] = ta.trend.SMAIndicator(close = self.close, n = 21 ).sma_indicator() 19 | 20 | 21 | # calculates the slow sma indicator (sma_slow) using ta library, period of 50 22 | def calculate_sma_slow(self): 23 | self.df['sma_slow'] = ta.trend.SMAIndicator(close = self.close, n = 50).sma_indicator() 24 | 25 | 26 | # calculates the mass index (mi) using ta library 27 | def calculate_mi(self): 28 | self.df['mi'] = ta.trend.MassIndex(high = self.high, low = self.low).mass_index() # n is left at the default of 9 and n2 is left at the default of 25 29 | 30 | 31 | def determine_signal(self, dframe): 32 | # initialise signal to hold: 0 33 | signal = 0 34 | 35 | # BUY SIGNAL: when mi drops below 26.5 and fast SMA is above the slow SMA and both are positively sloped 36 | if dframe['mi'].iloc[-1] < 26.5 and dframe['mi'].iloc[-2] >= 26.5 and dframe['sma_fast'].iloc[-1] > dframe['sma_slow'].iloc[-1] and dframe['sma_fast'].iloc[-1] > dframe['sma_fast'].iloc[-2] and dframe['sma_slow'].iloc[-1] > dframe['sma_slow'].iloc[-2]: 37 | signal = 1 38 | # SELL SIGNAL: when mi drops below 26.5 and slow SMA is above the fast SMA and both are negatively sloped 39 | elif dframe['mi'].iloc[-1] < 26.5 and dframe['mi'].iloc[-2] >= 26.5 and dframe['sma_fast'].iloc[-1] < dframe['sma_slow'].iloc[-1] and dframe['sma_fast'].iloc[-1] < dframe['sma_fast'].iloc[-2] and dframe['sma_slow'].iloc[-1] < dframe['sma_slow'].iloc[-2]: 40 | signal = -1 41 | 42 | return (signal, dframe['sma_fast'].iloc[-1] - dframe['sma_slow'].iloc[-1]) 43 | 44 | def run(self): 45 | self.calculate_sma_fast() 46 | self.calculate_sma_slow() 47 | self.calculate_mi() 48 | 49 | signal = self.determine_signal(self.df) 50 | 51 | return signal, self.df 52 | 53 | ''' The following methods are for plotting ''' 54 | 55 | def find_all_signals(self, plot_df): 56 | # assign intitial value of hold 57 | plot_df['signal'] = 0 58 | 59 | start = -1 * len( 60 | plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 61 | end = start + 51 # where the current window will stop (exclusive of the element at this index) 62 | 63 | # loop through data to determine all signals 64 | while end < 0: 65 | curr_window = plot_df[start:end] 66 | action = self.determine_signal(curr_window)[0] 67 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 68 | additional_info = self.determine_signal(curr_window)[1] 69 | plot_df.loc[plot_df.index[end - 1], 'additional_info'] = additional_info 70 | end += 1 71 | start += 1 72 | 73 | # compute final signal 74 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-51:])[0] 75 | 76 | def plot_graph(self): 77 | 78 | # deep copy of data so original is not impacted 79 | plot_df = self.df.copy(deep=True) 80 | 81 | # determine all signals for the dataset 82 | self.find_all_signals(plot_df) 83 | 84 | plot_df['zero_line'] = 0 85 | plot_df['mi_cross_line'] = 26.5 86 | 87 | # initialise visualisation object for plotting 88 | visualisation = v.Visualise(plot_df) 89 | 90 | # determining one buy signal example for plotting 91 | visualisation.determine_buy_marker() 92 | 93 | # determining one sell signal example for plotting 94 | visualisation.determine_sell_marker() 95 | 96 | # add subplots of senkou span A and B to form the ichimoku cloud, and the parabolic sar dots 97 | visualisation.add_subplot(plot_df['sma_fast'], color='orange', width=0.75) 98 | visualisation.add_subplot(plot_df['sma_slow'], color='blue', width=0.75) 99 | visualisation.add_subplot(plot_df['mi'], panel=1, color='m', width=0.75, ylabel='MI') 100 | visualisation.add_subplot(plot_df['mi_cross_line'], panel=1, color='k', secondary_y=False, width=0.75, linestyle='solid') 101 | 102 | # create final plot with title 103 | visualisation.plot_graph("SMA MI Strategy") 104 | -------------------------------------------------------------------------------- /trading_strategies/stochastic_oscillator_no_exit.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Sep 16 12:18:14 2020 4 | 5 | Some code snippets from pythonforfinance 6 | 7 | @author: mingy and caitlin 8 | 9 | This strategy uses the stochastic oscillator to determine buy and sell signals. 10 | The indicator evaluates the market's momentum and determines overbought and oversold conditions. 11 | """ 12 | 13 | import pandas as pd 14 | import mplfinance as mpf 15 | import numpy as np 16 | import trading_strategies.visualise as v 17 | 18 | 19 | 20 | class StochasticOscillatorNoExit: 21 | 22 | def __init__(self, file_path): 23 | self.max_window = 50 # set to 50 for better understanding of trend in graph. 24 | #self.df = pd.read_csv(file_path)[-self.max_window:] 25 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos"))[-self.max_window:] 26 | self.high = self.df['high'] 27 | self.close = self.df['close'] 28 | self.low = self.df['low'] 29 | 30 | # Calculate the low of the 14 previous periods 31 | def calculate_period_14_low(self): 32 | self.df['period_14_low'] = self.df['low'].rolling(window=14).min() 33 | 34 | # Calculate the high of the 14 previous periods 35 | def calculate_period_14_high(self): 36 | self.df['period_14_high'] = self.df['high'].rolling(window=14).max() 37 | 38 | # Calculate the current market rate expressed as a percentage 39 | def calculate_current_rate(self): 40 | self.df['current_rate'] = 100 * ((self.df['close'] - self.df['period_14_low']) / ( 41 | self.df['period_14_high'] - self.df['period_14_low'])) 42 | 43 | # Calculate the 3 period moving average of the market rate 44 | def calculate_period_3_average_rate(self): 45 | self.df['period_3_average_rate'] = self.df['current_rate'].rolling(window=3).mean() 46 | 47 | # determine whether to output a buy sell or hold signal 48 | def determine_signal(self): 49 | 50 | self.df['signal'] = 0 51 | 52 | # A buy entry signal is given when the market rate line passes up through the 3 period average line, and the market line is under 20 at that time. 53 | self.df.loc[(self.df['current_rate'] < 20) & (self.df['current_rate'] >= self.df['period_3_average_rate']) & ( 54 | self.df['current_rate'].shift(1) < self.df['period_3_average_rate'].shift(1)), 'signal'] = 1 55 | 56 | # A sell entry signal is given when the market line crosses down through the 3 period average line, and the market line is above 80 57 | self.df.loc[(self.df['current_rate'] > 80) & (self.df['current_rate'] <= self.df['period_3_average_rate']) & ( 58 | self.df['current_rate'].shift(1) > self.df['period_3_average_rate'].shift(1)), 'signal'] = -1 59 | 60 | signal_col = self.df.columns.get_loc('signal') 61 | return (self.df.iloc[-1, signal_col], self.df['current_rate'].iloc[-1]) 62 | 63 | # Runs the stochastic oscillator no exit strategy 64 | def run_stochastic_oscillator_no_exit(self): 65 | self.calculate_period_14_low() 66 | self.calculate_period_14_high() 67 | self.calculate_current_rate() 68 | self.calculate_period_3_average_rate() 69 | signal = self.determine_signal() 70 | return signal, self.df 71 | 72 | ''' The following methods are for plotting ''' 73 | 74 | def find_all_signals(self, plot_df): 75 | # assign intitial value of hold 76 | plot_df['signal'] = 0 77 | 78 | start = -1 * len( 79 | plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 80 | end = start + 50 # where the current window will stop (exclusive of the element at this index) 81 | 82 | # loop through data to determine all signals 83 | while end < 0: 84 | curr_window = plot_df[start:end] 85 | action = self.determine_signal(curr_window)[0] 86 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 87 | end += 1 88 | start += 1 89 | 90 | # compute final signal 91 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-50:])[0] 92 | 93 | def plot_graph(self): 94 | # deep copy of data so original is not impacted 95 | plot_df = self.df.copy(deep=True) 96 | 97 | # determine all signals for the dataset 98 | self.find_all_signals(plot_df) 99 | 100 | # initialise visualisation object for plotting 101 | visualisation = v.Visualise(plot_df) 102 | 103 | # determining one buy signal example for plotting 104 | visualisation.determine_buy_marker() 105 | 106 | # determining one sell signal example for plotting 107 | visualisation.determine_sell_marker() 108 | 109 | # add subplots of 200ema and awesome oscillator 110 | visualisation.add_subplot(plot_df['period_3_average_rate'], panel=1, color="orange") 111 | visualisation.add_subplot(plot_df['current_rate'], panel=1, color="blue") 112 | 113 | # create final plot with title 114 | visualisation.plot_graph("Stochastic Oscillator Trading Strategy") -------------------------------------------------------------------------------- /trading_strategies/trading_strategies: -------------------------------------------------------------------------------- 1 | Trading strategies send the buy, sell or hold signal to MQL5 2 | Based on the signal Metatrader 5 perform the trading 3 | 4 | How to run the Strategies: 5 | 1. open main_decisionmaker.py and import the trading strategy which is intended to run 6 | 2. Update the strategy and the signal line 7 | 3. Start the backtesting in Metatrader 5 8 | 4. Copy the python files along with the strategy folder(trading_strategies) into Metatrader 5 testing/files folder 9 | 5. copy other python files - main_decisionmaker.py, output.py and actionWriter.py 10 | 6. run main_decisionmaker.py 11 | -------------------------------------------------------------------------------- /trading_strategies/trix_ema.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import trading_strategies.visualise as v 4 | 5 | ''' 6 | The TRIX and EMA strategy uses the TRIX indicator (a triple smoothed 30 period EMA) and a Signal Line (9 period EMA of TRIX). 7 | The criteria to buy is when TRIX crosses above the signal line while negative. 8 | The criteria to sell is when TRIX crosses to below the signal line while positive. 9 | 10 | Author: Cheryl 11 | ''' 12 | class TrixEma: 13 | def __init__(self, file_path): 14 | self.max_window = 180 # set to 180 for better understanding of trend in graph. MIN value 99 15 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos"))[-self.max_window:] 16 | 17 | def calculate_trix(self): 18 | # initialise TRIX indicator for n = 30 19 | indicator_trix = ta.trend.TRIXIndicator(close = self.df['close'], window = 30) 20 | # calculate values 21 | self.df['trix'] = indicator_trix.trix() 22 | 23 | # signal line: 9 period EMA of TRIX 24 | def calculate_signal_line(self): 25 | # initialise EMA indicator for n = 9 26 | indicator_ema = ta.trend.EMAIndicator(close = self.df['trix'], window = 9) 27 | # calculate values 28 | self.df['signal_line'] = indicator_ema.ema_indicator() 29 | 30 | def determine_signal(self, dframe): 31 | action = 0 32 | trix_signal_dist = dframe['trix'].iloc[-1] - dframe['signal_line'].iloc[-1] 33 | # BUY CRITERIA: TRIX crosses to above the signal line (9 EMA) while below zero 34 | if dframe['trix'].iloc[-1] < 0 and dframe['trix'].iloc[-2] < dframe['signal_line'].iloc[-2] and dframe['trix'].iloc[-1] > dframe['signal_line'].iloc[-1]: 35 | action = 1 36 | # SELL CRITERIA: TRIX crosses to below the signal line (9 EMA) while above zero 37 | elif dframe['trix'].iloc[-1] > 0 and dframe['trix'].iloc[-2] > dframe['signal_line'].iloc[-2] and dframe['trix'].iloc[-1] < dframe['signal_line'].iloc[-1]: 38 | action = -1 39 | 40 | return (action, trix_signal_dist) 41 | 42 | def run_trix_ema(self): 43 | self.calculate_trix() 44 | self.calculate_signal_line() 45 | signal = self.determine_signal(self.df) 46 | return signal, self.df 47 | 48 | ''' The following methods are for plotting ''' 49 | 50 | def find_all_signals(self, plot_df): 51 | 52 | # assign intitial value of hold 53 | plot_df['signal'] = 0 54 | 55 | start = -1*len(plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 56 | end = start + 99 # where the current window will stop (exclusive of the element at this index) 57 | 58 | # loop through data to determine all signals 59 | while end < 0: 60 | curr_window = plot_df[start:end] 61 | action = self.determine_signal(curr_window)[0] 62 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 63 | end += 1 64 | start += 1 65 | 66 | # compute final signal 67 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-99:])[0] 68 | 69 | def plot_graph(self): 70 | 71 | # deep copy of data so original is not impacted 72 | plot_df = self.df.copy(deep=True) 73 | 74 | # determine all signals for the dataset 75 | self.find_all_signals(plot_df) 76 | 77 | # initialise visualisation object for plotting 78 | visualisation = v.Visualise(plot_df) 79 | 80 | # determining one buy signal example for plotting 81 | visualisation.determine_buy_marker() 82 | 83 | # determining one sell signal example for plotting 84 | visualisation.determine_sell_marker() 85 | 86 | # add subplots of TRIX and Signal Line 87 | visualisation.add_subplot(plot_df['signal_line'], color="royalblue", panel = 1, ylabel="Signal Line") 88 | visualisation.add_subplot(plot_df['trix'], panel=1, color="orange", ylabel="TRIX / Signal Line", secondary_y=False) 89 | 90 | # create final plot with title 91 | visualisation.plot_graph("TRIX and EMA Signal Line Strategy") -------------------------------------------------------------------------------- /trading_strategies/trix_mi.py: -------------------------------------------------------------------------------- 1 | # https://school.stockcharts.com/doku.php?id=technical_indicators:mass_index 2 | 3 | import pandas as pd 4 | import ta 5 | import trading_strategies.visualise as v 6 | 7 | class TrixMI: 8 | def __init__(self, file_path): 9 | self.df = pd.read_csv(file_path) 10 | self.high = self.df['high'] 11 | self.low = self.df['low'] 12 | self.close = self.df['close'] 13 | 14 | # calculates the trix indicator (trix) using ta library 15 | def calculate_trix(self): 16 | self.df['trix'] = ta.trend.TRIXIndicator(close = self.close).trix() # n is left at the default of 15 17 | 18 | # calculates the mass index (mi) using ta library 19 | def calculate_mi(self): 20 | self.df['mi'] = ta.trend.MassIndex(high = self.high, low = self.low).mass_index() # n is left at the default of 9 and n2 is left at the default of 25 21 | 22 | def determine_signal(self, dframe): 23 | 24 | # initialise all signals to hold: 0 25 | signal = 0 26 | 27 | # BUY SIGNAL: when mi drops below 26.5 and trix indicator was showing a downtrend, i.e. was below 0 28 | if dframe['mi'].iloc[-1] < 26.5 and dframe['mi'].iloc[-2] >= 26.5 and dframe['trix'].iloc[-1] < 0 : 29 | signal = 1 30 | # SELL SIGNAL: when mi drops below 26.5 and trix indicator was showing an uptrend, i.e. was above 0 31 | elif dframe['mi'].iloc[-1] < 26.5 and dframe['mi'].iloc[-2] >= 26.5 and dframe['trix'].iloc[-1] > 0: 32 | signal = -1 33 | 34 | return (signal, dframe['trix'].iloc[-2]) 35 | 36 | def run(self): 37 | self.calculate_trix() 38 | self.calculate_mi() 39 | signal = self.determine_signal(self.df) 40 | return signal, self.df 41 | 42 | ''' The following methods are for plotting ''' 43 | 44 | def find_all_signals(self, plot_df): 45 | # assign intitial value of hold 46 | plot_df['signal'] = 0 47 | 48 | start = -1 * len( 49 | plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 50 | end = start + 44 # where the current window will stop (exclusive of the element at this index) 51 | 52 | # loop through data to determine all signals 53 | while end < 0: 54 | curr_window = plot_df[start:end] 55 | action = self.determine_signal(curr_window)[0] 56 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 57 | additional_info = self.determine_signal(curr_window)[1] 58 | plot_df.loc[plot_df.index[end - 1], 'additional_info'] = additional_info 59 | end += 1 60 | start += 1 61 | 62 | # compute final signal 63 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-44:])[0] 64 | 65 | def plot_graph(self): 66 | 67 | # deep copy of data so original is not impacted 68 | plot_df = self.df.copy(deep=True) 69 | 70 | # determine all signals for the dataset 71 | self.find_all_signals(plot_df) 72 | 73 | plot_df['zero_line'] = 0 74 | plot_df['mi_cross_line'] = 26.5 75 | 76 | # initialise visualisation object for plotting 77 | visualisation = v.Visualise(plot_df) 78 | 79 | # determining one buy signal example for plotting 80 | visualisation.determine_buy_marker() 81 | 82 | # determining one sell signal example for plotting 83 | visualisation.determine_sell_marker() 84 | 85 | # add subplots of senkou span A and B to form the ichimoku cloud, and the parabolic sar dots 86 | visualisation.add_subplot(plot_df['trix'], panel=1, color="orange", ylabel="TRIX") 87 | visualisation.add_subplot(plot_df['zero_line'], panel=1, color="k", secondary_y=False, width=0.75, linestyle='solid') 88 | visualisation.add_subplot(plot_df['mi'], panel=2, color='m', width=0.75, ylabel='MI') 89 | visualisation.add_subplot(plot_df['mi_cross_line'], panel=2, color='k', secondary_y=False, width=0.75, linestyle='solid') 90 | 91 | # create final plot with title 92 | visualisation.plot_graph("TRIX MI Strategy") 93 | -------------------------------------------------------------------------------- /trading_strategies/trix_rsi.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import trading_strategies.visualise as v 4 | import numpy as np 5 | 6 | ''' 7 | author: Caitlin 8 | This strategy uses the TRIX indicator and the RSI indicator to determine buy/sell signals. 9 | TRIX is a triple smoothed Exponential Moving Average (EMA), eg the EMA of an EMA of an EMA. 10 | The Relative Strength Index is a momentum indicator measuring speed and change of price movements. 11 | ''' 12 | 13 | 14 | class TrixRsi: 15 | def __init__(self, file_path): 16 | #self.max_window = 200 # uncomment for graphing purposes 17 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos")) 18 | self.high = self.df['high'] 19 | self.close = self.df['close'] 20 | self.low = self.df['low'] 21 | 22 | def calculate_trix_indicator(self): 23 | # initialise trix indicator 24 | trix_ind = ta.trend.TRIXIndicator(close = self.close, window=15) 25 | self.df['trix'] = trix_ind.trix() 26 | 27 | def calculate_signal_line(self): 28 | #a 9 period ema on trix 29 | self.df['signal_line'] = ta.utils.ema(self.df['trix'], 9) 30 | 31 | def calculate_rsi(self): 32 | # initialise rsi indicator 33 | rsi_ind = ta.momentum.RSIIndicator(close=self.close) 34 | self.df['rsi'] = rsi_ind.rsi() 35 | 36 | def determine_signal(self, dframe): 37 | 38 | # initialise all signals to hold: 0 39 | dframe['signal'] = 0 40 | 41 | # BUY SIGNAL: if TRIX is below -0.01 and RSI shows oversold within last 3 candles place buy order 42 | dframe.loc[(((dframe['trix'] < -0.01) | (dframe['trix'].shift(1) < -0.01) | (dframe['trix'].shift(2) < -0.01)) 43 | & ((dframe['rsi'] < 35) | (dframe['rsi'].shift(1) < 35) | (dframe['rsi'].shift(2) < 35))), 'signal'] = 1 44 | 45 | # SELL SIGNAL: if TRIX is above 0.01 and RSI shows overbought within last 3 candles place sell order 46 | dframe.loc[(((dframe['trix'] > 0.01) | (dframe['trix'].shift(1) > 0.01) | (dframe['trix'].shift(2) > 0.01)) 47 | & ((dframe['rsi'] > 65) | (dframe['rsi'].shift(1) > 65) | (dframe['rsi'].shift(2) > 65))), 'signal'] = -1 48 | 49 | # return final data point's signal and the value of the trix signal line 50 | signal_col = dframe.columns.get_loc('signal') 51 | return (dframe.iloc[-1, signal_col], dframe['signal_line'].iloc[-1]) 52 | 53 | def run_trix_rsi(self): 54 | self.calculate_trix_indicator() 55 | self.calculate_signal_line() 56 | self.calculate_rsi() 57 | signal = self.determine_signal(self.df) 58 | return signal, self.df 59 | 60 | ''' The following methods are for plotting ''' 61 | 62 | def find_all_signals(self, plot_df): 63 | # assign intitial value of hold 64 | plot_df['signal'] = 0 65 | 66 | start = -1 * len( 67 | plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 68 | end = start + 200 # where the current window will stop (exclusive of the element at this index) 69 | 70 | # loop through data to determine all signals 71 | while end < 0: 72 | curr_window = plot_df[start:end] 73 | action = self.determine_signal(curr_window)[0] 74 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 75 | end += 1 76 | start += 1 77 | 78 | # compute final signal 79 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-200:])[0] 80 | 81 | def plot_graph(self): 82 | # deep copy of data so original is not impacted 83 | plot_df = self.df.copy(deep=True) 84 | 85 | # determine all signals for the dataset 86 | self.find_all_signals(plot_df) 87 | 88 | # initialise visualisation object for plotting 89 | visualisation = v.Visualise(plot_df) 90 | 91 | # determining one buy signal example for plotting 92 | visualisation.determine_buy_marker() 93 | 94 | # determining one sell signal example for plotting 95 | visualisation.determine_sell_marker() 96 | 97 | line_30 = [30] * self.max_window 98 | line_70 = [70] * self.max_window 99 | line_neg = [-0.01] * self.max_window 100 | line_pos = [0.01] * self.max_window 101 | 102 | # add subplots of 200ema and awesome oscillator 103 | visualisation.add_subplot(plot_df['trix'], panel=1, color="orange", ylabel="Trix Line") 104 | visualisation.add_subplot(plot_df['rsi'], panel=2, ylabel="Rsi") 105 | visualisation.add_subplot(line_30, panel=2) 106 | visualisation.add_subplot(line_70, panel=2) 107 | visualisation.add_subplot(line_neg, panel=1) 108 | visualisation.add_subplot(line_pos, panel=1) 109 | 110 | 111 | # create final plot with title 112 | visualisation.plot_graph("Trix Rsi Strategy") -------------------------------------------------------------------------------- /trading_strategies/tsi_crossover.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: vita 4 | https://www.investopedia.com/terms/t/tsi.asp 5 | """ 6 | import pandas as pd 7 | import ta 8 | import trading_strategies.visualise as v 9 | 10 | 11 | 12 | class TSICrossover: 13 | def __init__(self, file_path): 14 | self.df = pd.read_csv(file_path) 15 | # self.df = pd.DataFrame(inputs) 16 | self.close = self.df['close'] 17 | 18 | def add_tsi(self): 19 | self.df['tsi_line'] = ta.momentum.TSIIndicator(close=self.close).tsi() 20 | 21 | def calculate_tsi_signal(self): 22 | self.df['tsi_signal'] = self.df['tsi_line'].ewm(span=9, adjust=False).mean() 23 | 24 | def determine_signal(self, dframe): 25 | line = dframe['tsi_line'] 26 | signal = dframe['tsi_signal'] 27 | 28 | action = 0 29 | 30 | # SELL CRITERIA: if TSI line and signal line has crossed above 0 and TSI line crosses signal 31 | if (line.iloc[-1] > 0 and signal.iloc[-1] > 0 and line.iloc[-2] > 0 and signal.iloc[-2] > 0) and \ 32 | ((line.iloc[-1] < signal.iloc[-1] and line.iloc[-2] > signal.iloc[-2]) or ( 33 | line.iloc[-1] > signal.iloc[-1] and line.iloc[-2] < signal.iloc[-2])): 34 | action = -1 35 | 36 | # BUY CRITERIA: if TSI line and signal line is below 0 and tsi crosses signal line 37 | if (line.iloc[-1] < 0 and signal.iloc[-1] < 0 and line.iloc[-2] < 0 and signal.iloc[-2] < 0) and \ 38 | ((line.iloc[-1] > signal.iloc[-1] and line.iloc[-2] < signal.iloc[-2]) or ( 39 | line.iloc[-1] < signal.iloc[-1] and line.iloc[-2] > signal.iloc[-2])): 40 | action = 1 41 | 42 | return action, line.iloc[-1] - signal.iloc[-1], 43 | 44 | def run_tsi_crossover(self): 45 | self.add_tsi() 46 | self.calculate_tsi_signal() 47 | return self.determine_signal(self.df) 48 | 49 | ''' The following methods are for plotting ''' 50 | def find_all_signals(self, plot_df): 51 | # assign initial value of hold 52 | plot_df['signal'] = 0 53 | 54 | start = -1 * len(plot_df) 55 | end = start + 40 56 | 57 | # loop through data to determine all signals 58 | while end < 0: 59 | curr_window = plot_df[start:end] 60 | action = self.determine_signal(curr_window)[0] 61 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 62 | end += 1 63 | start += 1 64 | 65 | action = self.determine_signal(plot_df[-40:])[0] 66 | plot_df.loc[plot_df.index[-1], 'signal'] = action 67 | 68 | def plot_graph(self): 69 | # create shallow copy for plotting so as to not accidentally impact original df 70 | plot_df = self.df.copy(deep=False) 71 | 72 | self.find_all_signals(plot_df) 73 | 74 | plot_df['0_line'] = 0 75 | 76 | # initialise visualisation object for plotting 77 | visualisation = v.Visualise(plot_df) 78 | 79 | # determining one buy signal example for plotting 80 | visualisation.determine_buy_marker() 81 | 82 | # determining one sell signal example for plotting 83 | visualisation.determine_sell_marker() 84 | 85 | # add subplots 86 | visualisation.add_subplot(plot_df['tsi_line'], panel=1, color='b', width=0.75, ylabel='TSI Line\n (blue)') 87 | visualisation.add_subplot(plot_df['tsi_signal'], panel=1, color='pink', width=0.75, ylabel='TSI Signal\n (pink)') 88 | visualisation.add_subplot(plot_df['0_line'], panel=1, color='k', secondary_y=False, width=0.75, linestyle='solid') 89 | 90 | # create final plot with title 91 | visualisation.plot_graph("TSI Crossover Strategy") 92 | 93 | -------------------------------------------------------------------------------- /trading_strategies/visualise.py: -------------------------------------------------------------------------------- 1 | import mplfinance as mpf 2 | import pandas as pd 3 | import numpy as np 4 | 5 | ''' 6 | This Visualise class is used to create plots for the trading strategies in our library. Use it by instantiating the class, and calling the desired methods. 7 | 8 | Author: Cheryl 9 | ''' 10 | 11 | class Visualise: 12 | def __init__(self, plot_df): 13 | self.plot_df = plot_df 14 | self.subplots = [] 15 | self.range = max(self.plot_df['high']) - min(self.plot_df['low']) 16 | self.plot_df.index = pd.to_datetime(self.plot_df.datetime) # if your data uses column name date, change this line to pd.to_datetime(self.plot_df.date) 17 | 18 | def add_subplot(self,series, **kwargs): 19 | self.subplots.append(mpf.make_addplot(series, **kwargs)) 20 | 21 | def determine_buy_marker(self): 22 | self.plot_df['buy_marker'] = np.nan 23 | # get index of first buy example in input dataset 24 | buy_index_ls = self.plot_df.index[(self.plot_df['signal'] == 1)].tolist() 25 | 26 | # if a buy example exists 27 | for i in buy_index_ls: 28 | self.plot_df.loc[i, 'buy_marker'] = self.plot_df.loc[i, 'low'] - self.range * 0.06 29 | #break # comment out this break if you would like to see all buy signals 30 | 31 | def determine_sell_marker(self): 32 | self.plot_df['sell_marker'] = np.nan 33 | # get index of first sell example in input dataset 34 | sell_index_ls = self.plot_df.index[(self.plot_df['signal'] == -1)].tolist() 35 | # if sell example exists 36 | for i in sell_index_ls: 37 | self.plot_df.loc[i, 'sell_marker'] = self.plot_df.loc[i, 'high'] + self.range * 0.06 38 | #break # comment out this break if you would like to see all sell signals 39 | 40 | def plot_graph(self, name): 41 | # add buy and sell marker if they exist 42 | if not self.plot_df['buy_marker'].isnull().all(): 43 | self.subplots.append(mpf.make_addplot(self.plot_df['buy_marker'], type = "scatter", marker="^", markersize = 60, color="green")) 44 | if not self.plot_df['sell_marker'].isnull().all(): 45 | self.subplots.append(mpf.make_addplot(self.plot_df['sell_marker'], type = "scatter", marker="v", markersize = 60, color="red")) 46 | 47 | # Create colour style candlestick colours to green and red 48 | mc = mpf.make_marketcolors(up='g',down='r') 49 | 50 | # Set colour and grid style 51 | s = mpf.make_mpf_style(marketcolors=mc) 52 | 53 | # view plot 54 | mpf.plot(self.plot_df, type="candle", addplot=self.subplots, style=s, title=name) 55 | -------------------------------------------------------------------------------- /trading_strategies/vortex_crossover.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import trading_strategies.visualise as v 4 | 5 | # Source: https://school.stockcharts.com/doku.php?id=technical_indicators:vortex_indicator 6 | 7 | class VortexCrossover: 8 | def __init__(self, file_path): 9 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos")) 10 | self.high = self.df['high'] 11 | self.close = self.df['close'] 12 | self.low = self.df['low'] 13 | 14 | def calculate_vortex(self): 15 | # calculate -VI 16 | self.df['-vi'] = ta.trend.VortexIndicator(high=self.high, low=self.low, close=self.close,window=14).vortex_indicator_neg() 17 | # calculate +VI 18 | self.df['+vi'] = ta.trend.VortexIndicator(high=self.high, low=self.low, close=self.close, window=14).vortex_indicator_pos() 19 | 20 | 21 | def determine_signal(self, dframe): 22 | action = 0 23 | # BUY CRITERIA: +vi crosses above -vi 24 | if dframe['+vi'].iloc[-1] > dframe['-vi'].iloc[-1] and dframe['+vi'].iloc[-2] <= dframe['-vi'].iloc[-2]: 25 | action = 1 26 | # SELL CRITERIA: -vi crosses above +vi 27 | elif dframe['+vi'].iloc[-1] < dframe['-vi'].iloc[-1] and dframe['+vi'].iloc[-2] >= dframe['-vi'].iloc[-2]: 28 | action = -1 29 | return (action, dframe['close'].iloc[-1]) 30 | 31 | def run(self): 32 | self.calculate_vortex() 33 | signal = self.determine_signal(self.df) 34 | return signal, self.df 35 | 36 | ''' 37 | The following methods are for plotting. 38 | ''' 39 | 40 | def find_all_signals(self, plot_df): 41 | # assign intitial value of hold 42 | plot_df['signal'] = 0 43 | 44 | start = -1 * len(plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 45 | end = start + 16 # where the current window will stop (exclusive of the element at this index) 46 | 47 | # loop through data to determine all signals 48 | while end < 0: 49 | curr_window = plot_df[start:end] 50 | action = self.determine_signal(curr_window)[0] 51 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 52 | additional_info = self.determine_signal(curr_window)[1] 53 | plot_df.loc[plot_df.index[end - 1], 'additional_info'] = additional_info 54 | end += 1 55 | start += 1 56 | 57 | # compute final signal 58 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-16:])[0] 59 | 60 | def plot_graph(self): 61 | 62 | # deep copy of data so original is not impacted 63 | plot_df = self.df.copy(deep=True) 64 | 65 | # determine all signals for the dataset 66 | self.find_all_signals(plot_df) 67 | 68 | # zero line series for horizontal axis at value 0 in bull power and bear power 69 | plot_df['zero_line'] = 0 70 | 71 | # initialise visualisation object for plotting 72 | visualisation = v.Visualise(plot_df) 73 | 74 | # determining one buy signal example for plotting 75 | visualisation.determine_buy_marker() 76 | 77 | # determining one sell signal example for plotting 78 | visualisation.determine_sell_marker() 79 | 80 | # add subplots of senkou span A and B to form the ichimoku cloud, and the parabolic sar dots 81 | visualisation.add_subplot(plot_df['+vi'], panel = 1, color="green", width=1, ylabel='VI') 82 | visualisation.add_subplot(plot_df['-vi'], panel = 1, color="red", width=1) 83 | 84 | # create final plot with title 85 | visualisation.plot_graph("Vortex Crossover Strategy") 86 | -------------------------------------------------------------------------------- /trading_strategies/vortex_sma.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import ta 3 | import trading_strategies.visualise as v 4 | 5 | ''' 6 | The Vortex indicator (VI) comprises two oscillators which illustrate positive (+VI) and negative trend movements (-VI). 7 | We buy when +VI crosses above -VI (indicating a bullish trend) and close price is above 50 SMA. 8 | We sell when -VI crosses above +VI (indicating a bearish trend) and close price is below 50 SMA. 9 | 10 | Author: Cheryl 11 | ''' 12 | 13 | class VortexSma: 14 | def __init__(self, file_path): 15 | self.max_window = 150 # set to 100 for better understanding of trend in graph. MIN value 50 16 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos"))[-self.max_window:] 17 | self.high = self.df['high'] 18 | self.close = self.df['close'] 19 | self.low = self.df['low'] 20 | 21 | def calculate_sma(self): 22 | # initialise SMA indicator for 50 time periods 23 | indicator_sma = ta.trend.SMAIndicator(close = self.close, window = 50) 24 | # Calculate 50 sma 25 | self.df['50_sma'] = indicator_sma.sma_indicator() 26 | 27 | def calculate_vortex(self): 28 | # initialise vortex indicator 29 | indicator_vortex = ta.trend.VortexIndicator(high = self.high, low = self.low, close = self.close, window = 14) 30 | # calculate -VI 31 | self.df['-vi'] = indicator_vortex.vortex_indicator_neg() 32 | # calculate +VI 33 | self.df['+vi'] = indicator_vortex.vortex_indicator_pos() 34 | 35 | def determine_signal(self, dframe): 36 | sma_dist = dframe['close'].iloc[-1] - dframe['50_sma'].iloc[-1] 37 | action = 0 38 | # BUY CRITERIA: +vi crosses above -vi, and price is above 50sma 39 | if dframe['+vi'].iloc[-2] <= dframe['-vi'].iloc[-2] and dframe['+vi'].iloc[-1] > dframe['-vi'].iloc[-1] and dframe['close'].iloc[-1] > dframe['50_sma'].iloc[-1]: 40 | action = 1 41 | # SELL CRITERIA: -vi crosses above +vi, and price is below 50sma 42 | elif dframe['-vi'].iloc[-2] <= dframe['+vi'].iloc[-2] and dframe['-vi'].iloc[-1] > dframe['+vi'].iloc[-1] and dframe['close'].iloc[-1] < dframe['50_sma'].iloc[-1]: 43 | action = -1 44 | return (action, sma_dist) 45 | 46 | def run_vortex_sma(self): 47 | self.calculate_sma() 48 | self.calculate_vortex() 49 | signal = self.determine_signal(self.df) 50 | return signal, self.df 51 | 52 | ''' The following methods are for plotting ''' 53 | 54 | def find_all_signals(self, plot_df): 55 | # assign intitial value of hold 56 | plot_df['signal'] = 0 57 | 58 | start = -1*len(plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 59 | end = start + 50 # where the current window will stop (exclusive of the element at this index) 60 | 61 | # loop through data to determine all signals 62 | while end < 0: 63 | curr_window = plot_df[start:end] 64 | action = self.determine_signal(curr_window)[0] 65 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 66 | end += 1 67 | start += 1 68 | 69 | # compute final signal 70 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-50:])[0] 71 | 72 | def plot_graph(self): 73 | 74 | # deep copy of data so original is not impacted 75 | plot_df = self.df.copy(deep=True) 76 | 77 | # determine all signals for the dataset 78 | self.find_all_signals(plot_df) 79 | 80 | # initialise visualisation object for plotting 81 | visualisation = v.Visualise(plot_df) 82 | 83 | # determining one buy signal example for plotting 84 | visualisation.determine_buy_marker() 85 | 86 | # determining one sell signal example for plotting 87 | visualisation.determine_sell_marker() 88 | 89 | # add subplots of 50 SMA, +VI and -VI 90 | visualisation.add_subplot(plot_df['50_sma'], color="royalblue") 91 | visualisation.add_subplot(plot_df['+vi'], panel=1, color="seagreen", ylabel="Vortex Indicator") 92 | visualisation.add_subplot(plot_df['-vi'], panel=1, color="darkred", secondary_y=False) 93 | 94 | # create final plot with title 95 | visualisation.plot_graph("Vortex Indicator and 50 SMA Strategy") -------------------------------------------------------------------------------- /trading_strategies/williams_rsi.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 4 | @author: caitlin 5 | 6 | This strategy uses the Williams%R indicator. This momentum indicator oscillates between 0 and -100, and shows 7 | how the current price compares to a 14 day look back period, where a reading near -100 indicates the market is 8 | near the lowest low and a reading near 0 indicates the market is near the highest high. This strategy combines 9 | an 8 period rsi. 10 | """ 11 | 12 | 13 | import pandas as pd 14 | import trading_strategies.visualise as v 15 | import ta 16 | 17 | 18 | class WilliamsRsi: 19 | 20 | # loading the data in from file_path 21 | def __init__(self, file_path): 22 | # self.max_window = 150 #uncomment for graphing purposes 23 | self.df = pd.DataFrame(file_path, columns=("time", "open", "high", "low", "close", "tick_volume","pos")) 24 | self.high = self.df['high'] 25 | self.low = self.df['low'] 26 | self.close = self.df['close'] 27 | 28 | # Calculate the williams indicator 29 | def calculate_williams_indicator(self): 30 | will_ind = ta.momentum.WilliamsRIndicator(high = self.high, low = self.low, close = self.close) 31 | self.df['williams_indicator'] = will_ind.wr() 32 | 33 | 34 | def calculate_rsi(self): 35 | rsi_ind = ta.momentum.RSIIndicator(self.df['close'], window = 8) 36 | self.df['rsi'] = rsi_ind.rsi() 37 | 38 | 39 | def determine_signal(self, dframe): 40 | dframe['signal'] = 0 41 | 42 | # BUY signal: when williams indicator is less than -70 and rsi is less than 30 within the last 3 candles 43 | dframe.loc[(((dframe['williams_indicator'].shift(2) < -70) | (dframe['williams_indicator'].shift(1) < -70) | (dframe['williams_indicator'] < -70) | (dframe['williams_indicator'].shift(3) < -70)) 44 | & ((dframe['rsi'] < 30) | (dframe['rsi'].shift(1) < 30) | (dframe['rsi'].shift(2) < 30))), 'signal'] = 1 45 | 46 | # SELL signal: when williams indicator is greater than -30 and rsi is greater than 70 within last 3 candles 47 | dframe.loc[(((dframe['williams_indicator'].shift(2) > -30) | (dframe['williams_indicator'].shift(1) > -30) | (dframe['williams_indicator'] > -30) | (dframe['williams_indicator'].shift(3) > -30)) 48 | & ((dframe['rsi'] > 70) | (dframe['rsi'].shift(1) > 70) | (dframe['rsi'].shift(2) > 70))), 'signal'] = -1 49 | 50 | # return buy sell or hold signal and the value of the williams indicator 51 | signal_col = dframe.columns.get_loc('signal') 52 | return (dframe.iloc[-1, signal_col], dframe['williams_indicator'].iloc[-1]) 53 | 54 | # Runs the indicator 55 | def run_williams_indicator(self): 56 | self.calculate_williams_indicator() 57 | 58 | self.calculate_rsi() 59 | 60 | signal = self.determine_signal(self.df) 61 | return signal, self.df 62 | 63 | ''' The following methods are for plotting ''' 64 | 65 | def find_all_signals(self, plot_df): 66 | # assign initial value of hold 67 | plot_df['signal'] = 0 68 | 69 | start = -1 * len( 70 | plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 71 | end = start + 100 # where the current window will stop (exclusive of the element at this index) 72 | 73 | # loop through data to determine all signals 74 | while end < 0: 75 | curr_window = plot_df[start:end] 76 | action = self.determine_signal(curr_window)[0] 77 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 78 | end += 1 79 | start += 1 80 | 81 | # compute final signal 82 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-100:])[0] 83 | 84 | def plot_graph(self): 85 | # deep copy of data so original is not impacted 86 | plot_df = self.df.copy(deep=True) 87 | 88 | # determine all signals for the dataset 89 | self.find_all_signals(plot_df) 90 | 91 | # initialise visualisation object for plotting 92 | visualisation = v.Visualise(plot_df) 93 | 94 | # determining one buy signal example for plotting 95 | visualisation.determine_buy_marker() 96 | 97 | # determining one sell signal example for plotting 98 | visualisation.determine_sell_marker() 99 | 100 | line_30 = [30] * self.max_window 101 | line_70 = [70] * self.max_window 102 | 103 | # add subplots of williams indicator and rsi 104 | visualisation.add_subplot(plot_df['williams_indicator'], panel=1, color="blue") 105 | visualisation.add_subplot(line_70, panel=1, color="green") 106 | visualisation.add_subplot(line_30, panel=1, color="black") 107 | visualisation.add_subplot(plot_df['rsi'], panel=2) 108 | visualisation.add_subplot(line_70, panel=2, color="green") 109 | visualisation.add_subplot(line_30, panel=2, color="black") 110 | 111 | 112 | # create final plot with title 113 | visualisation.plot_graph("Williams Rsi Trading Strategy") 114 | 115 | 116 | -------------------------------------------------------------------------------- /trading_strategies/zig_zag.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | import mplfinance as mpf 5 | import ta 6 | import trading_strategies.visualise as v 7 | 8 | ''' 9 | ### Author: Wilson ### 10 | Strategy from: 11 | https://www.investopedia.com/terms/z/zig_zag_indicator.asp 12 | 13 | Self-implemented. 14 | 15 | ''' 16 | 17 | class ZigZag: 18 | 19 | # constructor 20 | def __init__(self, file_path): 21 | self.df = pd.read_csv(file_path) 22 | #self.df = pd.DataFrame(file_path) 23 | 24 | # determine and signal for particular index 25 | def determine_signal(self, dframe): 26 | 27 | signal = 0 28 | 29 | #BUY if higher highs and higher lows (against a 10 period backdrop) 30 | if((dframe['high'].iloc[-10:-1].max() > dframe['high'].iloc[-20:-11].max()) & 31 | (dframe['low'].iloc[-10:-1].min() > dframe['low'].iloc[-20:-11].min())): 32 | signal = 1 33 | 34 | #SELL if lower highers and lower lows 35 | if((dframe['high'].iloc[-10:-1].max() < dframe['high'].iloc[-20:-11].max()) & 36 | (dframe['low'].iloc[-10:-1].min() < dframe['low'].iloc[-20:-11].min())): 37 | signal = -1 38 | 39 | return signal 40 | 41 | # determine and return additional useful information 42 | def determine_additional_info(self, dframe): 43 | 44 | return dframe['high'].iloc[-10:-1].max() - dframe['high'].iloc[-20:-11].max() 45 | 46 | # run strategy 47 | def run_zigzag(self): 48 | 49 | # generate data for return tuple 50 | signal = self.determine_signal(self.df) 51 | additional_info = self.determine_additional_info(self.df) 52 | 53 | # create return tuple and append data 54 | result = [] 55 | result.append(signal) 56 | result.append(additional_info) 57 | 58 | return tuple(result), self.df 59 | 60 | ''' The following methods are for plotting ''' 61 | 62 | def find_all_signals(self, plot_df): 63 | # assign intitial value of hold 64 | plot_df['signal'] = 0 65 | 66 | start = -1 * len( 67 | plot_df) # using negative indices just in case you are using a subset of input data where index does not start at index 0 68 | end = start + 60 # where the current window will stop (exclusive of the element at this index) 69 | 70 | # loop through data to determine all signals 71 | while end < 0: 72 | curr_window = plot_df[start:end] 73 | action = self.determine_signal(curr_window) 74 | plot_df.loc[plot_df.index[end - 1], 'signal'] = action 75 | end += 1 76 | start += 1 77 | 78 | # compute final signal 79 | plot_df.loc[plot_df.index[-1], 'signal'] = self.determine_signal(plot_df[-60:]) 80 | 81 | def plot_graph(self): 82 | # deep copy of data so original is not impacted 83 | plot_df = self.df.copy(deep=True) 84 | 85 | # determine all signals for the dataset 86 | self.find_all_signals(plot_df) 87 | 88 | # initialise visualisation object for plotting 89 | visualisation = v.Visualise(plot_df) 90 | 91 | # determining one buy signal example for plotting 92 | visualisation.determine_buy_marker() 93 | 94 | # determining one sell signal example for plotting 95 | visualisation.determine_sell_marker() 96 | 97 | # create final plot with title 98 | visualisation.plot_graph("Zig Zag Trading Strategy") 99 | --------------------------------------------------------------------------------