From df7e4d2f275fda876f9fb11a7cf042a8afea167a Mon Sep 17 00:00:00 2001 From: didier Date: Sun, 21 Mar 2021 20:48:50 +0000 Subject: [PATCH] Add sentiment analysis comparison and correlation --- .../comparison_analysis/ca_controller.py | 28 +- .../comparison_analysis/finbrain_api.py | 272 ++++++++++++++++++ 2 files changed, 294 insertions(+), 6 deletions(-) create mode 100644 gamestonk_terminal/comparison_analysis/finbrain_api.py diff --git a/gamestonk_terminal/comparison_analysis/ca_controller.py b/gamestonk_terminal/comparison_analysis/ca_controller.py index e158e504e05..d08e39b682e 100644 --- a/gamestonk_terminal/comparison_analysis/ca_controller.py +++ b/gamestonk_terminal/comparison_analysis/ca_controller.py @@ -6,12 +6,13 @@ import requests from typing import List from datetime import datetime import pandas as pd - +from matplotlib import pyplot as plt from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal import config_terminal as cfg from gamestonk_terminal.helper_funcs import get_flair, parse_known_args_and_warn from gamestonk_terminal.comparison_analysis import yahoo_finance_api as yf_api from gamestonk_terminal.comparison_analysis import market_watch_api as mw_api +from gamestonk_terminal.comparison_analysis import finbrain_api as f_api from gamestonk_terminal.menu import session from prompt_toolkit.completion import NestedCompleter @@ -27,10 +28,12 @@ class ComparisonAnalysisController: "get", "select", "historical", - "corr", + "hcorr", "income", "balance", "cashflow", + "sentiment", + "scorr", ] def __init__( @@ -84,13 +87,16 @@ class ComparisonAnalysisController: print(" select select similar companies") print("") if self.similar: - print(" historical historical data comparison") - print(" corr correlation between similar companies") + print(" historical historical price data comparison") + print(" hcorr historical price correlation") print("") print(" income income financials comparison") print(" balance balance financials comparison") print(" cashflow cashflow comparison") print("") + print(" sentiment sentiment analysis comparison") + print(" scorr sentiment correlation") + print("") return @staticmethod @@ -201,8 +207,8 @@ class ComparisonAnalysisController: other_args, self.stock, self.ticker, self.start, self.interval, self.similar ) - def call_corr(self, other_args: List[str]): - """Process correlation command""" + def call_hcorr(self, other_args: List[str]): + """Process historical correlation command""" yf_api.correlation( other_args, self.stock, self.ticker, self.start, self.interval, self.similar ) @@ -219,6 +225,14 @@ class ComparisonAnalysisController: """Process cashflow command""" mw_api.compare_cashflow(other_args, self.ticker, self.similar) + def call_sentiment(self, other_args: List[str]): + """Process sentiment command""" + f_api.sentiment_compare(other_args, self.ticker, self.similar) + + def call_scorr(self, other_args: List[str]): + """Process sentiment correlation command""" + f_api.sentiment_correlation(other_args, self.ticker, self.similar) + def menu(stock: pd.DataFrame, ticker: str, start: datetime, interval: str): """Comparison Analysis Menu""" @@ -242,6 +256,8 @@ def menu(stock: pd.DataFrame, ticker: str, start: datetime, interval: str): an_input = input(f"{get_flair()} (ca)> ") try: + plt.close("all") + process_input = ca_controller.switch(an_input) if process_input is not None: diff --git a/gamestonk_terminal/comparison_analysis/finbrain_api.py b/gamestonk_terminal/comparison_analysis/finbrain_api.py new file mode 100644 index 00000000000..45740ae44fe --- /dev/null +++ b/gamestonk_terminal/comparison_analysis/finbrain_api.py @@ -0,0 +1,272 @@ +""" Comparison Analysis Finbrain API """ +__docformat__ = "numpy" + +import argparse +from typing import List +import requests +from matplotlib import pyplot as plt +import matplotlib.dates as mdates +import pandas as pd +from pandas.plotting import register_matplotlib_converters +import numpy as np +import seaborn as sns +from gamestonk_terminal.helper_funcs import ( + parse_known_args_and_warn, + plot_autoscale, +) +from gamestonk_terminal.config_plot import PLOT_DPI +from gamestonk_terminal import feature_flags as gtff + + +register_matplotlib_converters() + + +def sentiment_compare(other_args: List[str], ticker: str, similar: List[str]): + """Plot sentiments across similar companies + + Parameters + ---------- + other_args : List[str] + Command line arguments to be processed with argparse + ticker : str + Main ticker to compare income + similar : List[str] + Similar companies to compare income with + """ + parser = argparse.ArgumentParser( + add_help=False, + prog="sentiment_compare", + description=""" + FinBrain's sentiment comparison across similar tickers. + Finbrain collects the news headlines from 15+ major financial news + sources on a daily basis and analyzes them to generate sentiment scores + for more than 4500 US stocks.FinBrain Technologies develops deep learning + algorithms for financial analysis and prediction, which currently serves + traders from more than 150 countries all around the world. + [Source: https://finbrain.tech] + """, + ) + parser.add_argument( + "-s", + "--similar", + dest="l_similar", + type=lambda s: [str(item).upper() for item in s.split(",")], + default=similar, + help="similar companies to compare with.", + ) + parser.add_argument( + "-a", + "--also", + dest="l_also", + type=lambda s: [str(item).upper() for item in s.split(",")], + default=[], + help="apart from loaded similar companies also compare with.", + ) + + try: + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + l_similar = ns_parser.l_similar + l_similar += ns_parser.l_also + + # Add main ticker to similar list of companies + l_similar = [ticker] + l_similar + + df_sentiment = get_sentiments(l_similar) + + if not df_sentiment.empty: + plot_sentiments(df_sentiment, l_similar) + + print("") + + except Exception as e: + print(e, "\n") + return + + +def sentiment_correlation(other_args: List[str], ticker: str, similar: List[str]): + """Plot correlation sentiments across similar companies + + Parameters + ---------- + other_args : List[str] + Command line arguments to be processed with argparse + ticker : str + Main ticker to compare income + similar : List[str] + Similar companies to compare income with + """ + parser = argparse.ArgumentParser( + add_help=False, + prog="sentiment_compare", + description=""" + FinBrain's sentiment correlation across similar tickers. + Finbrain collects the news headlines from 15+ major financial news + sources on a daily basis and analyzes them to generate sentiment scores + for more than 4500 US stocks.FinBrain Technologies develops deep learning + algorithms for financial analysis and prediction, which currently serves + traders from more than 150 countries all around the world. + [Source: https://finbrain.tech] + """, + ) + parser.add_argument( + "-s", + "--similar", + dest="l_similar", + type=lambda s: [str(item).upper() for item in s.split(",")], + default=similar, + help="similar companies to compare with.", + ) + parser.add_argument( + "-a", + "--also", + dest="l_also", + type=lambda s: [str(item).upper() for item in s.split(",")], + default=[], + help="apart from loaded similar companies also compare with.", + ) + + try: + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + l_similar = ns_parser.l_similar + l_similar += ns_parser.l_also + + # Add main ticker to similar list of companies + l_similar = [ticker] + l_similar + + df_sentiment = get_sentiments(l_similar) + + if not df_sentiment.empty: + plot_correlation(df_sentiment, l_similar) + + print("") + + except Exception as e: + print(e, "\n") + return + + +def get_sentiments(similar: List[str]) -> pd.DataFrame: + """Gets Sentiment analysis from several tickers provided by FinBrain's API + + Parameters + ---------- + similar : List[str] + Similar tickers to get the sentiment analysis from + + Returns + ------- + DataFrame() + Contains sentiment analysis from several tickers + """ + df_sentiment = pd.DataFrame() + for ticker in similar: + result = requests.get(f"https://api.finbrain.tech/v0/sentiments/{ticker}") + if result.status_code == 200: + if "sentimentAnalysis" in result.json(): + df_sentiment[ticker] = [ + float(val) + for val in list(result.json()["sentimentAnalysis"].values()) + ] + else: + print(f"Unexpected data format from FinBrain API for {ticker}") + + else: + print(f"Request error in retrieving {ticker} sentiment from FinBrain API") + + if not df_sentiment.empty: + df_sentiment.index = list(result.json()["sentimentAnalysis"].keys()) + df_sentiment.sort_index(ascending=True, inplace=True) + + return df_sentiment + + +def plot_sentiments(df_sentiment: pd.DataFrame, similar: List[str]): + """Plot Sentiment analysis comparison between similar companies + + Parameters + ---------- + df_sentiment : pd.DataFrame + Dataframe with sentiment analysis from several similar companies + similar : List[str] + List of similar companies + """ + plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI) + + for idx, ticker in enumerate(similar): + offset = 2 * idx + plt.axhline(y=offset, color="k", linestyle="--", lw=2) + plt.axhline(y=offset + 1, color="k", linestyle="--", lw=1) + + senValues = np.array(pd.to_numeric(df_sentiment[ticker].values)) + senNone = np.array(0 * len(df_sentiment)) + plt.fill_between( + df_sentiment.index, + pd.to_numeric(df_sentiment[ticker].values) + offset, + offset, + where=(senValues < senNone), + alpha=0.60, + color="red", + interpolate=True, + ) + + plt.fill_between( + df_sentiment.index, + pd.to_numeric(df_sentiment[ticker].values) + offset, + offset, + where=(senValues >= senNone), + alpha=0.60, + color="green", + interpolate=True, + ) + + plt.xlabel("Time") + plt.ylabel("Sentiment") + plt.axhline(y=-1, color="k", linestyle="--", lw=1) + plt.grid(b=True, which="major", color="#666666", linestyle="-") + plt.minorticks_on() + plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2) + plt.yticks(np.arange(len(similar)) * 2, similar) + plt.title(f"FinBrain's Sentiment Analysis since {df_sentiment.index[0]}") + plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=1)) + plt.gcf().autofmt_xdate() + + if gtff.USE_ION: + plt.ion() + plt.show() + + +def plot_correlation(df_sentiment: pd.DataFrame, similar: List[str]): + """Plot Sentiment correlation between similar companies + + Parameters + ---------- + df_sentiment : pd.DataFrame + Dataframe with sentiment analysis from several similar companies + similar : List[str] + List of similar companies + """ + plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI) + + mask = np.zeros((len(similar), len(similar)), dtype=bool) + mask[np.triu_indices(len(mask))] = True + + sns.heatmap( + df_sentiment.corr(), + cbar_kws={"ticks": [-1.0, -0.5, 0.0, 0.5, 1.0]}, + cmap="RdYlGn", + linewidths=1, + annot=True, + vmin=-1, + vmax=1, + mask=mask, + ) + + if gtff.USE_ION: + plt.ion() + plt.show()