Exemple #1
0
    def __init__(self, args):
        plt.style.use('seaborn-white')
        plt.rc('grid', linestyle="dotted", color='#a0a0a0')
        plt.rcParams['axes.edgecolor'] = "#04383F"
        plt.rcParams['figure.figsize'] = (12, 6)

        print("\n--* Eiten has been initialized...")
        self.args = args

        # Create data engine
        self.dataEngine = DataEngine(args)

        # Monto carlo simulator
        self.simulator = MontoCarloSimulator()

        # Strategy manager
        self.strategyManager = StrategyManager()

        # Back tester
        self.backTester = BackTester()

        # Data dictionary
        self.data_dictionary = {}

        print('\n')
Exemple #2
0
    def __init__(self):
        print("\n--* Eiten has been initialized...")
        self.HISTORY_TO_USE = history_to_use
        self.DATA_GRANULARITY_MINUTES = data_granularity_minutes
        self.FUTURE_BARS_FOR_TESTING = future_bars
        self.APPLY_NOISE_FILTERING = apply_noise_filtering
        self.IS_LONG_ONLY_PORTFOLIO = is_only_long
        self.MARKET_INDEX = market_index
        self.IS_TEST = is_test
        self.EIGEN_PORTFOLIO_NUMBER = eigen_portfolio_number
        self.STOCKS_FILE_PATH = stocks_file_path

        # Create data engine
        self.dataEngine = DataEngine(self.HISTORY_TO_USE,
                                     self.DATA_GRANULARITY_MINUTES,
                                     self.FUTURE_BARS_FOR_TESTING,
                                     self.MARKET_INDEX, self.IS_TEST,
                                     self.STOCKS_FILE_PATH)

        # Monto carlo simulator
        self.simulator = MontoCarloSimulator()

        # Strategy manager
        self.strategyManager = StrategyManager()

        # Back tester
        self.backTester = BackTester()

        # Data dictionary
        self.data_dictionary = {}

        print('\n')
Exemple #3
0
    def load_data(self):
        """
        Loads data needed for analysis
        """
        # Gather data for all stocks in a dictionary format
        # Dictionary keys will be -> historical, future
        de = DataEngine(self.args)
        self.data_dict = de.collect_data_for_all_tickers()
        p, f = de.get_data(self.args.market_index)

        self.market_data["historical"] = pd.DataFrame(
            columns=[self.args.market_index], data=p)
        self.market_data["future"] = pd.DataFrame(
            columns=[self.args.market_index], data=f)
        # Get return matrices and vectors
        return self.data_dict
Exemple #4
0
	def __init__(self):
		print("Surpriver has been initialized...")
		self.TOP_PREDICTIONS_TO_PRINT = top_n
		self.HISTORY_TO_USE = history_to_use
		self.MINIMUM_VOLUME = min_volume
		self.IS_LOAD_FROM_DICTIONARY = is_load_from_dictionary
		self.DATA_DICTIONARY_PATH = data_dictionary_path
		self.IS_SAVE_DICTIONARY = is_save_dictionary
		self.DATA_GRANULARITY_MINUTES = data_granularity_minutes
		self.IS_TEST = is_test
		self.FUTURE_BARS_FOR_TESTING = future_bars
		self.VOLATILITY_FILTER = volatility_filter

		# Create data engine
		self.dataEngine = DataEngine(self.HISTORY_TO_USE, self.DATA_GRANULARITY_MINUTES, 
							self.IS_SAVE_DICTIONARY, self.IS_LOAD_FROM_DICTIONARY, self.DATA_DICTIONARY_PATH,
							self.MINIMUM_VOLUME,
							self.IS_TEST, self.FUTURE_BARS_FOR_TESTING,
							self.VOLATILITY_FILTER)
Exemple #5
0
class Eiten:
    def __init__(self):
        print("\n--* Eiten has been initialized...")
        self.HISTORY_TO_USE = history_to_use
        self.DATA_GRANULARITY_MINUTES = data_granularity_minutes
        self.FUTURE_BARS_FOR_TESTING = future_bars
        self.APPLY_NOISE_FILTERING = apply_noise_filtering
        self.IS_LONG_ONLY_PORTFOLIO = is_only_long
        self.MARKET_INDEX = market_index
        self.IS_TEST = is_test
        self.EIGEN_PORTFOLIO_NUMBER = eigen_portfolio_number
        self.STOCKS_FILE_PATH = stocks_file_path

        # Create data engine
        self.dataEngine = DataEngine(self.HISTORY_TO_USE,
                                     self.DATA_GRANULARITY_MINUTES,
                                     self.FUTURE_BARS_FOR_TESTING,
                                     self.MARKET_INDEX, self.IS_TEST,
                                     self.STOCKS_FILE_PATH)

        # Monto carlo simulator
        self.simulator = MontoCarloSimulator()

        # Strategy manager
        self.strategyManager = StrategyManager()

        # Back tester
        self.backTester = BackTester()

        # Data dictionary
        self.data_dictionary = {}

        print('\n')

    def calculate_percentage_change(self, old, new):
        """
		Calculate percentage change
		"""
        return ((new - old) * 100) / old

    def create_returns(self, historical_price_info):
        """
		Create log return matrix, percentage return matrix, and mean return vector
		"""

        returns_matrix = []
        returns_matrix_percentages = []
        predicted_return_vectors = []
        for i in range(0, len(historical_price_info)):
            close_prices = list(historical_price_info[i]["Close"])
            log_returns = [
                math.log(close_prices[i] / close_prices[i - 1])
                for i in range(1, len(close_prices))
            ]
            percentage_returns = [
                self.calculate_percentage_change(close_prices[i - 1],
                                                 close_prices[i])
                for i in range(1, len(close_prices))
            ]

            total_data = len(close_prices)
            # Expected returns in future. We can either use historical returns as future returns on try to simulate future returns and take the mean. For simulation, you can modify the functions in simulator to use here.
            future_expected_returns = np.mean([
                (self.calculate_percentage_change(
                    close_prices[i - 1], close_prices[i])) / (total_data - i)
                for i in range(1, len(close_prices))
            ])  # More focus on recent returns

            # Add to matrices
            returns_matrix.append(log_returns)
            returns_matrix_percentages.append(percentage_returns)

            # Add returns to vector
            predicted_return_vectors.append(
                future_expected_returns
            )  # Assuming that future returns are similar to past returns

        # Convert to numpy arrays for one liner calculations
        predicted_return_vectors = np.array(predicted_return_vectors)
        returns_matrix = np.array(returns_matrix)
        returns_matrix_percentages = np.array(returns_matrix_percentages)

        return predicted_return_vectors, returns_matrix, returns_matrix_percentages

    def load_data(self):
        """
		Loads data needed for analysis
		"""
        # Gather data for all stocks in a dictionary format
        # Dictionary keys will be -> historical_prices, future_prices
        self.data_dictionary = self.dataEngine.collect_data_for_all_tickers()

        # Add data to lists
        symbol_names = list(sorted(self.data_dictionary.keys()))
        historical_price_info, future_prices = [], []
        for symbol in symbol_names:
            historical_price_info.append(
                self.data_dictionary[symbol]["historical_prices"])
            future_prices.append(self.data_dictionary[symbol]["future_prices"])

        # Get return matrices and vectors
        predicted_return_vectors, returns_matrix, returns_matrix_percentages = self.create_returns(
            historical_price_info)
        return historical_price_info, future_prices, symbol_names, predicted_return_vectors, returns_matrix, returns_matrix_percentages

    def run_strategies(self):
        """
		Run strategies, back and future test them, and simulate the returns.
		"""
        historical_price_info, future_prices, symbol_names, predicted_return_vectors, returns_matrix, returns_matrix_percentages = self.load_data(
        )
        historical_price_market, future_prices_market = self.dataEngine.get_market_index_price(
        )

        # Calculate covariance matrix
        covariance_matrix = np.cov(returns_matrix)

        # Use random matrix theory to filter out the noisy eigen values
        if self.APPLY_NOISE_FILTERING:
            print(
                "\n** Applying random matrix theory to filter out noise in the covariance matrix...\n"
            )
            covariance_matrix = self.strategyManager.random_matrix_theory_based_cov(
                returns_matrix)

        # Get weights for the portfolio
        eigen_portfolio_weights_dictionary = self.strategyManager.calculate_eigen_portfolio(
            symbol_names, covariance_matrix, self.EIGEN_PORTFOLIO_NUMBER)
        mvp_portfolio_weights_dictionary = self.strategyManager.calculate_minimum_variance_portfolio(
            symbol_names, covariance_matrix)
        msr_portfolio_weights_dictionary = self.strategyManager.calculate_maximum_sharpe_portfolio(
            symbol_names, covariance_matrix, predicted_return_vectors)
        ga_portfolio_weights_dictionary = self.strategyManager.calculate_genetic_algo_portfolio(
            symbol_names, returns_matrix_percentages)

        # Print weights
        print("\n*% Printing portfolio weights...")
        self.print_and_plot_portfolio_weights(
            eigen_portfolio_weights_dictionary, 'Eigen Portfolio', plot_num=1)
        self.print_and_plot_portfolio_weights(
            mvp_portfolio_weights_dictionary,
            'Minimum Variance Portfolio (MVP)',
            plot_num=2)
        self.print_and_plot_portfolio_weights(msr_portfolio_weights_dictionary,
                                              'Maximum Sharpe Portfolio (MSR)',
                                              plot_num=3)
        self.print_and_plot_portfolio_weights(ga_portfolio_weights_dictionary,
                                              'Genetic Algo (GA)',
                                              plot_num=4)
        self.draw_plot()

        # Back test
        print("\n*& Backtesting the portfolios...")
        self.backTester.back_test(symbol_names,
                                  eigen_portfolio_weights_dictionary,
                                  self.data_dictionary,
                                  historical_price_market,
                                  self.IS_LONG_ONLY_PORTFOLIO,
                                  market_chart=True,
                                  strategy_name='Eigen Portfolio')
        self.backTester.back_test(
            symbol_names,
            mvp_portfolio_weights_dictionary,
            self.data_dictionary,
            historical_price_market,
            self.IS_LONG_ONLY_PORTFOLIO,
            market_chart=False,
            strategy_name='Minimum Variance Portfolio (MVP)')
        self.backTester.back_test(
            symbol_names,
            msr_portfolio_weights_dictionary,
            self.data_dictionary,
            historical_price_market,
            self.IS_LONG_ONLY_PORTFOLIO,
            market_chart=False,
            strategy_name='Maximum Sharpe Portfolio (MSR)')
        self.backTester.back_test(symbol_names,
                                  ga_portfolio_weights_dictionary,
                                  self.data_dictionary,
                                  historical_price_market,
                                  self.IS_LONG_ONLY_PORTFOLIO,
                                  market_chart=False,
                                  strategy_name='Genetic Algo (GA)')
        self.draw_plot()

        if self.IS_TEST:
            print("\n#^ Future testing the portfolios...")
            # Future test
            self.backTester.future_test(symbol_names,
                                        eigen_portfolio_weights_dictionary,
                                        self.data_dictionary,
                                        future_prices_market,
                                        self.IS_LONG_ONLY_PORTFOLIO,
                                        market_chart=True,
                                        strategy_name='Eigen Portfolio')
            self.backTester.future_test(
                symbol_names,
                mvp_portfolio_weights_dictionary,
                self.data_dictionary,
                future_prices_market,
                self.IS_LONG_ONLY_PORTFOLIO,
                market_chart=False,
                strategy_name='Minimum Variance Portfolio (MVP)')
            self.backTester.future_test(
                symbol_names,
                msr_portfolio_weights_dictionary,
                self.data_dictionary,
                future_prices_market,
                self.IS_LONG_ONLY_PORTFOLIO,
                market_chart=False,
                strategy_name='Maximum Sharpe Portfolio (MSR)')
            self.backTester.future_test(symbol_names,
                                        ga_portfolio_weights_dictionary,
                                        self.data_dictionary,
                                        future_prices_market,
                                        self.IS_LONG_ONLY_PORTFOLIO,
                                        market_chart=False,
                                        strategy_name='Genetic Algo (GA)')
            self.draw_plot()

        # Simulation
        print("\n+$ Simulating future prices using monte carlo...")
        self.simulator.simulate_portfolio(symbol_names,
                                          eigen_portfolio_weights_dictionary,
                                          self.data_dictionary,
                                          future_prices_market,
                                          self.IS_TEST,
                                          market_chart=True,
                                          strategy_name='Eigen Portfolio')
        self.simulator.simulate_portfolio(
            symbol_names,
            eigen_portfolio_weights_dictionary,
            self.data_dictionary,
            future_prices_market,
            self.IS_TEST,
            market_chart=False,
            strategy_name='Minimum Variance Portfolio (MVP)')
        self.simulator.simulate_portfolio(
            symbol_names,
            eigen_portfolio_weights_dictionary,
            self.data_dictionary,
            future_prices_market,
            self.IS_TEST,
            market_chart=False,
            strategy_name='Maximum Sharpe Portfolio (MSR)')
        self.simulator.simulate_portfolio(symbol_names,
                                          ga_portfolio_weights_dictionary,
                                          self.data_dictionary,
                                          future_prices_market,
                                          self.IS_TEST,
                                          market_chart=False,
                                          strategy_name='Genetic Algo (GA)')
        self.draw_plot()

    def draw_plot(self):
        """
		Draw plots
		"""
        plt.tight_layout()
        plt.grid()
        plt.legend(fontsize=14)
        plt.show()

    def print_and_plot_portfolio_weights(self, weights_dictionary, strategy,
                                         plot_num):
        print("\n-------- Weights for %s --------" % strategy)
        symbols = list(sorted(weights_dictionary.keys()))
        symbol_weights = []
        for symbol in symbols:
            print("Symbol: %s, Weight: %.4f" %
                  (symbol, weights_dictionary[symbol]))
            symbol_weights.append(weights_dictionary[symbol])

        # Plot
        width = 0.1
        x = np.arange(len(symbol_weights))
        plt.bar(x + (width * (plot_num - 1)) + 0.05,
                symbol_weights,
                label=strategy,
                width=width)
        plt.xticks(x, symbols, fontsize=14)
        plt.yticks(fontsize=14)
        plt.xlabel("Symbols", fontsize=14)
        plt.ylabel("Weight in Portfolio", fontsize=14)
        plt.title("Portfolio Weights for Different Strategies", fontsize=14)
Exemple #6
0
class Surpriver:
	def __init__(self):
		print("Surpriver has been initialized...")
		self.TOP_PREDICTIONS_TO_PRINT = top_n
		self.HISTORY_TO_USE = history_to_use
		self.MINIMUM_VOLUME = min_volume
		self.IS_LOAD_FROM_DICTIONARY = is_load_from_dictionary
		self.DATA_DICTIONARY_PATH = data_dictionary_path
		self.IS_SAVE_DICTIONARY = is_save_dictionary
		self.DATA_GRANULARITY_MINUTES = data_granularity_minutes
		self.IS_TEST = is_test
		self.FUTURE_BARS_FOR_TESTING = future_bars
		self.VOLATILITY_FILTER = volatility_filter

		# Create data engine
		self.dataEngine = DataEngine(self.HISTORY_TO_USE, self.DATA_GRANULARITY_MINUTES, 
							self.IS_SAVE_DICTIONARY, self.IS_LOAD_FROM_DICTIONARY, self.DATA_DICTIONARY_PATH,
							self.MINIMUM_VOLUME,
							self.IS_TEST, self.FUTURE_BARS_FOR_TESTING,
							self.VOLATILITY_FILTER)
		

	def is_nan(self, object):
		"""
		Checks if a value is null. 
		"""
		return object != object

	def calculate_percentage_change(self, old, new):
		return ((new - old) * 100) / old

	def calculate_return(self, old, new):
		return new / old

	def parse_large_values(self, value):
		if value < 1000:
			value = str(value)
		elif value >= 1000 and value < 1000000:
			value = round(value / 1000, 2)
			value = str(value) + "K"
		else:
			value = round(value / 1000000, 1)
			value = str(value) + "M"

		return value

	def calculate_volume_changes(self, historical_price):
		volume = list(historical_price["Volume"])
		dates = list(historical_price["Datetime"])
		dates = [str(date) for date in dates]

		# Get volume by date
		volume_by_date_dictionary = collections.defaultdict(list)
		for j in range(0, len(volume)):
			date = dates[j].split(" ")[0]
			volume_by_date_dictionary[date].append(volume[j])

		for key in volume_by_date_dictionary:
			volume_by_date_dictionary[key] = np.sum(volume_by_date_dictionary[key]) # taking average as we have multiple bars per day. 

		# Get all dates
		all_dates = list(reversed(sorted(volume_by_date_dictionary.keys())))
		latest_date = all_dates[0]
		latest_data_point =  list(reversed(sorted(dates)))[0]

		# Get volume information
		today_volume = volume_by_date_dictionary[latest_date]
		average_vol_last_five_days = np.mean([volume_by_date_dictionary[date] for date in all_dates[1:6]])
		average_vol_last_twenty_days = np.mean([volume_by_date_dictionary[date] for date in all_dates[1:20]])

		
		return latest_data_point, self.parse_large_values(today_volume), self.parse_large_values(average_vol_last_five_days), self.parse_large_values(average_vol_last_twenty_days)

	def calculate_recent_volatility(self, historical_price):
		close_price = list(historical_price["Close"])
		volatility_five_bars = np.std(close_price[-5:])
		volatility_twenty_bars = np.std(close_price[-20:])
		volatility_all = np.std(close_price)
		return volatility_five_bars, volatility_twenty_bars, volatility_all

	def calculate_future_performance(self, future_data):
		CLOSE_PRICE_INDEX = 4
		price_at_alert = future_data[0][CLOSE_PRICE_INDEX]
		prices_in_future = [item[CLOSE_PRICE_INDEX] for item in future_data[1:]]
		prices_in_future = [item for item in prices_in_future if item != 0]
		total_sum_percentage_change = abs(sum([self.calculate_percentage_change(price_at_alert, next_price) for next_price in prices_in_future]))
		future_volatility = np.std(prices_in_future)
		return total_sum_percentage_change, future_volatility

	def find_anomalies(self):
		"""
		Main function that does everything
		"""

		# Gather data for all stocks
		if self.IS_LOAD_FROM_DICTIONARY == 0:
			features, historical_price_info, future_prices, symbol_names = self.dataEngine.collect_data_for_all_tickers()
		else:
			# Load data from dictionary
			features, historical_price_info, future_prices, symbol_names = self.dataEngine.load_data_from_dictionary()
		
		# Find anomalous stocks using the Isolation Forest model. Read more about the model at -> https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.IsolationForest.html
		detector = IsolationForest(n_estimators = 100, random_state = 0)
		detector.fit(features)
		predictions = detector.decision_function(features)
		
		# Print top predictions with some statistics
		predictions_with_output_data = [[predictions[i], symbol_names[i], historical_price_info[i], future_prices[i]] for i in range(0, len(predictions))]
		predictions_with_output_data = list(sorted(predictions_with_output_data))

		for item in predictions_with_output_data[:self.TOP_PREDICTIONS_TO_PRINT]:
			# Get some stats to print
			prediction, symbol, historical_price, future_price = item

			# Check if future data is present or not
			if self.IS_TEST == 1 and len(future_price) < 5:
				print("No future data is present. Please make sure that you ran the prior command with is_test enabled or disable that command now. Exiting now...")
				exit()

			latest_date, today_volume, average_vol_last_five_days, average_vol_last_twenty_days = self.calculate_volume_changes(historical_price)
			volatility_vol_last_five_days, volatility_vol_last_twenty_days, _ = self.calculate_recent_volatility(historical_price)
			if average_vol_last_five_days == None or volatility_vol_last_five_days == None:
				continue

			if self.IS_TEST == 0:
				# Not testing so just print the predictions
				print("Last Bar Time: %s\nSymbol: %s\nAnomaly Score: %.3f\nToday Volume: %s\nAverage Volume 5d: %s\nAverage Volume 20d: %s\nVolatility 5bars: %.3f\nVolatility 20bars: %.3f\n----------------------" % 
																	(latest_date, symbol, prediction,
																	today_volume, average_vol_last_five_days, average_vol_last_twenty_days,
																	volatility_vol_last_five_days, volatility_vol_last_twenty_days))
			else:
				# Testing so show what happened in the future
				future_abs_sum_percentage_change, _ = self.calculate_future_performance(future_price)
				print("Last Bar Time: %s\nSymbol: %s\nAnomaly Score: %.3f\nToday Volume: %s\nAverage Volume 5d: %s\nAverage Volume 20d: %s\nVolatility 5bars: %.3f\nVolatility 20bars: %.3f\nFuture Absolute Sum Price Changes: %.2f\n----------------------" % 
																	(latest_date, symbol, prediction,
																	today_volume, average_vol_last_five_days, average_vol_last_twenty_days,
																	volatility_vol_last_five_days, volatility_vol_last_twenty_days,
																	future_abs_sum_percentage_change))

		if self.IS_TEST == 1:
			self.calculate_future_stats(predictions_with_output_data)

	def calculate_future_stats(self, predictions_with_output_data):
		"""
		Calculate different stats for future data to show whether the anomalous stocks found were actually better than non-anomalous ones
		"""
		future_change = []
		anomalous_score = []
		historical_volatilities = []
		future_volatilities = []

		for item in predictions_with_output_data:
			prediction, symbol, historical_price, future_price = item
			future_sum_percentage_change, future_volatility = self.calculate_future_performance(future_price)
			_, _, historical_volatility = self.calculate_recent_volatility(historical_price)

			# Skip for when there is a reverse split, the yfinance package does not handle that well so percentages get weirdly large
			if abs(future_sum_percentage_change) > 250 or self.is_nan(future_sum_percentage_change) == True or self.is_nan(prediction) == True:
				continue

			future_change.append(future_sum_percentage_change)
			anomalous_score.append(prediction)
			future_volatilities.append(future_volatility)
			historical_volatilities.append(historical_volatility)

		# Calculate correlation and stats
		correlation = np.corrcoef(anomalous_score, future_change)[0, 1]
		anomalous_future_changes = np.mean([future_change[x] for x in range(0, len(future_change)) if anomalous_score[x] < 0]) # Anything less than 0 is considered anomalous
		normal_future_changes = np.mean([future_change[x] for x in range(0, len(future_change)) if anomalous_score[x] >= 0])
		anomalous_future_volatilities = np.mean([future_volatilities[x] for x in range(0, len(future_volatilities)) if anomalous_score[x] < 0]) # Anything less than 0 is considered anomalous
		normal_future_volatilities = np.mean([future_volatilities[x] for x in range(0, len(future_volatilities)) if anomalous_score[x] >= 0])
		anomalous_historical_volatilities = np.mean([historical_volatilities[x] for x in range(0, len(historical_volatilities)) if anomalous_score[x] < 0]) # Anything less than 0 is considered anomalous
		normal_historical_volatilities = np.mean([historical_volatilities[x] for x in range(0, len(historical_volatilities)) if anomalous_score[x] >= 0])
		
		print("\n*************** Future Performance ***************")
		print("Correlation between future absolute change vs anomalous score (lower is better, range = (-1, 1)): **%.2f**\nTotal absolute change in future for Anomalous Stocks: **%.3f**\nTotal absolute change in future for Normal Stocks: **%.3f**\nAverage future volatility of Anomalous Stocks: **%.3f**\nAverage future volatility of Normal Stocks: **%.3f**\nHistorical volatility for Anomalous Stocks: **%.3f**\nHistorical volatility for Normal Stocks: **%.3f**\n" % (
								correlation,
								anomalous_future_changes, normal_future_changes, 
								anomalous_future_volatilities, normal_future_volatilities,
								anomalous_historical_volatilities, normal_historical_volatilities))

		# Plot
		FONT_SIZE = 14
		colors = ['#c91414' if anomalous_score[x] < 0 else '#035AA6' for x in range(0, len(anomalous_score))]
		anomalous_vs_normal = np.array([1 if anomalous_score[x] < 0 else 0 for x in range(0, len(anomalous_score))])
		plt.scatter(np.array(anomalous_score)[anomalous_vs_normal == 1], np.array(future_change)[anomalous_vs_normal == 1], marker='v', color = '#c91414')
		plt.scatter(np.array(anomalous_score)[anomalous_vs_normal == 0], np.array(future_change)[anomalous_vs_normal == 0], marker='P', color = '#035AA6')
		plt.axvline(x = 0, linestyle = '--', color = '#848484')
		plt.xlabel("Anomaly Score", fontsize = FONT_SIZE)
		plt.ylabel("Absolute Future Change", fontsize = FONT_SIZE)
		plt.xticks(fontsize = FONT_SIZE)
		plt.yticks(fontsize = FONT_SIZE)
		plt.legend(["Anomalous", "Normal"], fontsize = FONT_SIZE)
		plt.title("Absolute Future Change", fontsize = FONT_SIZE)
		plt.tight_layout()
		plt.grid()
		plt.show()
Exemple #7
0
class Eiten:
    def __init__(self, args):
        plt.style.use('seaborn-white')
        plt.rc('grid', linestyle="dotted", color='#a0a0a0')
        plt.rcParams['axes.edgecolor'] = "#04383F"
        plt.rcParams['figure.figsize'] = (12, 6)

        print("\n--* Eiten has been initialized...")
        self.args = args

        # Create data engine
        self.dataEngine = DataEngine(args)

        # Monto carlo simulator
        self.simulator = MontoCarloSimulator()

        # Strategy manager
        self.strategyManager = StrategyManager()

        # Back tester
        self.backTester = BackTester()

        # Data dictionary
        self.data_dictionary = {}

        print('\n')

    def calculate_percentage_change(self, old, new):
        """
        Calculate percentage change
        """
        return ((new - old) * 100) / old

    def create_returns(self, historical_price_info):
        """
        Create log return matrix, percentage return matrix, and mean return 
        vector
        """

        returns_matrix = []
        returns_matrix_percentages = []
        predicted_return_vectors = []
        for i in range(0, len(historical_price_info)):
            close_prices = list(historical_price_info[i]["Close"])
            log_returns = [
                math.log(close_prices[i] / close_prices[i - 1])
                for i in range(1, len(close_prices))
            ]
            percentage_returns = [
                self.calculate_percentage_change(close_prices[i - 1],
                                                 close_prices[i])
                for i in range(1, len(close_prices))
            ]

            total_data = len(close_prices)

            # Expected returns in future. We can either use historical returns as future returns on try to simulate future returns and take the mean. For simulation, you can modify the functions in simulator to use here.
            future_expected_returns = np.mean([
                (self.calculate_percentage_change(
                    close_prices[i - 1], close_prices[i])) / (total_data - i)
                for i in range(1, len(close_prices))
            ])  # More focus on recent returns

            # Add to matrices
            returns_matrix.append(log_returns)
            returns_matrix_percentages.append(percentage_returns)

            # Add returns to vector
            # Assuming that future returns are similar to past returns
            predicted_return_vectors.append(future_expected_returns)

        # Convert to numpy arrays for one liner calculations
        predicted_return_vectors = np.array(predicted_return_vectors)
        returns_matrix = np.array(returns_matrix)
        returns_matrix_percentages = np.array(returns_matrix_percentages)

        return predicted_return_vectors, returns_matrix, returns_matrix_percentages

    def load_data(self):
        """
        Loads data needed for analysis
        """
        # Gather data for all stocks in a dictionary format
        # Dictionary keys will be -> historical_prices, future_prices
        self.data_dictionary = self.dataEngine.collect_data_for_all_tickers()

        # Add data to lists
        symbol_names = list(sorted(self.data_dictionary.keys()))
        historical_price_info, future_prices = [], []
        for symbol in symbol_names:
            historical_price_info.append(
                self.data_dictionary[symbol]["historical_prices"])
            future_prices.append(self.data_dictionary[symbol]["future_prices"])

        # Get return matrices and vectors
        predicted_return_vectors, returns_matrix, returns_matrix_percentages = self.create_returns(
            historical_price_info)
        return historical_price_info, future_prices, symbol_names, predicted_return_vectors, returns_matrix, returns_matrix_percentages

    def run_strategies(self):
        """
        Run strategies, back and future test them, and simulate the returns.
        """
        historical_price_info, future_prices, symbol_names, predicted_return_vectors, returns_matrix, returns_matrix_percentages = self.load_data(
        )
        historical_price_market, future_prices_market = self.dataEngine.get_market_index_price(
        )

        # Calculate covariance matrix
        covariance_matrix = np.cov(returns_matrix)

        # Use random matrix theory to filter out the noisy eigen values
        if self.args.apply_noise_filtering:
            print(
                "\n** Applying random matrix theory to filter out noise in the covariance matrix...\n"
            )
            covariance_matrix = self.strategyManager.random_matrix_theory_based_cov(
                returns_matrix)

        # Get weights for the portfolio
        eigen_portfolio_weights_dictionary = self.strategyManager.calculate_eigen_portfolio(
            symbol_names, covariance_matrix, self.args.eigen_portfolio_number)
        mvp_portfolio_weights_dictionary = self.strategyManager.calculate_minimum_variance_portfolio(
            symbol_names, covariance_matrix)
        msr_portfolio_weights_dictionary = self.strategyManager.calculate_maximum_sharpe_portfolio(
            symbol_names, covariance_matrix, predicted_return_vectors)
        ga_portfolio_weights_dictionary = self.strategyManager.calculate_genetic_algo_portfolio(
            symbol_names, returns_matrix_percentages)

        # Print weights
        print("\n*% Printing portfolio weights...")
        self.print_and_plot_portfolio_weights(
            eigen_portfolio_weights_dictionary, 'Eigen Portfolio', plot_num=1)
        self.print_and_plot_portfolio_weights(
            mvp_portfolio_weights_dictionary,
            'Minimum Variance Portfolio (MVP)',
            plot_num=2)
        self.print_and_plot_portfolio_weights(msr_portfolio_weights_dictionary,
                                              'Maximum Sharpe Portfolio (MSR)',
                                              plot_num=3)
        self.print_and_plot_portfolio_weights(ga_portfolio_weights_dictionary,
                                              'Genetic Algo (GA)',
                                              plot_num=4)
        self.draw_plot("output/weights.png")

        # Back test
        print("\n*& Backtesting the portfolios...")
        self.backTester.back_test(symbol_names,
                                  eigen_portfolio_weights_dictionary,
                                  self.data_dictionary,
                                  historical_price_market,
                                  self.args.only_long,
                                  market_chart=True,
                                  strategy_name='Eigen Portfolio')
        self.backTester.back_test(
            symbol_names,
            mvp_portfolio_weights_dictionary,
            self.data_dictionary,
            historical_price_market,
            self.args.only_long,
            market_chart=False,
            strategy_name='Minimum Variance Portfolio (MVP)')
        self.backTester.back_test(
            symbol_names,
            msr_portfolio_weights_dictionary,
            self.data_dictionary,
            historical_price_market,
            self.args.only_long,
            market_chart=False,
            strategy_name='Maximum Sharpe Portfolio (MSR)')
        self.backTester.back_test(symbol_names,
                                  ga_portfolio_weights_dictionary,
                                  self.data_dictionary,
                                  historical_price_market,
                                  self.args.only_long,
                                  market_chart=False,
                                  strategy_name='Genetic Algo (GA)')
        self.draw_plot("output/backtest.png")

        if self.args.is_test:
            print("\n#^ Future testing the portfolios...")
            # Future test
            self.backTester.future_test(symbol_names,
                                        eigen_portfolio_weights_dictionary,
                                        self.data_dictionary,
                                        future_prices_market,
                                        self.args.only_long,
                                        market_chart=True,
                                        strategy_name='Eigen Portfolio')
            self.backTester.future_test(
                symbol_names,
                mvp_portfolio_weights_dictionary,
                self.data_dictionary,
                future_prices_market,
                self.args.only_long,
                market_chart=False,
                strategy_name='Minimum Variance Portfolio (MVP)')
            self.backTester.future_test(
                symbol_names,
                msr_portfolio_weights_dictionary,
                self.data_dictionary,
                future_prices_market,
                self.args.only_long,
                market_chart=False,
                strategy_name='Maximum Sharpe Portfolio (MSR)')
            self.backTester.future_test(symbol_names,
                                        ga_portfolio_weights_dictionary,
                                        self.data_dictionary,
                                        future_prices_market,
                                        self.args.only_long,
                                        market_chart=False,
                                        strategy_name='Genetic Algo (GA)')
            self.draw_plot("output/future_tests.png")

        # Simulation
        print("\n+$ Simulating future prices using monte carlo...")
        self.simulator.simulate_portfolio(symbol_names,
                                          eigen_portfolio_weights_dictionary,
                                          self.data_dictionary,
                                          future_prices_market,
                                          self.args.is_test,
                                          market_chart=True,
                                          strategy_name='Eigen Portfolio')
        self.simulator.simulate_portfolio(
            symbol_names,
            eigen_portfolio_weights_dictionary,
            self.data_dictionary,
            future_prices_market,
            self.args.is_test,
            market_chart=False,
            strategy_name='Minimum Variance Portfolio (MVP)')
        self.simulator.simulate_portfolio(
            symbol_names,
            eigen_portfolio_weights_dictionary,
            self.data_dictionary,
            future_prices_market,
            self.args.is_test,
            market_chart=False,
            strategy_name='Maximum Sharpe Portfolio (MSR)')
        self.simulator.simulate_portfolio(symbol_names,
                                          ga_portfolio_weights_dictionary,
                                          self.data_dictionary,
                                          future_prices_market,
                                          self.args.is_test,
                                          market_chart=False,
                                          strategy_name='Genetic Algo (GA)')
        self.draw_plot("output/monte_carlo.png")

    def draw_plot(self, filename="output/graph.png"):
        """
        Draw plots
        """
        # Styling for plots

        plt.grid()
        plt.legend(fontsize=14)
        plt.tight_layout()
        plt.show()

        """if self.args.save_plot:
            plt.savefig(filename)
        else:
            plt.tight_layout()
            plt.show()""" # Plots were not being generated properly. Need to fix this.

    def print_and_plot_portfolio_weights(self, weights_dictionary: dict,
                                         strategy, plot_num: int) -> None:
        print("\n-------- Weights for %s --------" % strategy)
        symbols = list(sorted(weights_dictionary.keys()))
        symbol_weights = []
        for symbol in symbols:
            print("Symbol: %s, Weight: %.4f" %
                  (symbol, weights_dictionary[symbol]))
            symbol_weights.append(weights_dictionary[symbol])

        # Plot
        width = 0.1
        x = np.arange(len(symbol_weights))
        plt.bar(x + (width * (plot_num - 1)) + 0.05,
                symbol_weights,
                label=strategy,
                width=width)
        plt.xticks(x, symbols, fontsize=14)
        plt.yticks(fontsize=14)
        plt.xlabel("Symbols", fontsize=14)
        plt.ylabel("Weight in Portfolio", fontsize=14)
        plt.title("Portfolio Weights for Different Strategies", fontsize=14)