diff --git a/openbb_terminal/common/behavioural_analysis/twitter_view.py b/openbb_terminal/common/behavioural_analysis/twitter_view.py index 201ac0b5027..2a7c19818bd 100644 --- a/openbb_terminal/common/behavioural_analysis/twitter_view.py +++ b/openbb_terminal/common/behavioural_analysis/twitter_view.py @@ -15,7 +15,7 @@ import openbb_terminal.config_plot as cfg_plot from openbb_terminal.config_terminal import theme from openbb_terminal.common.behavioural_analysis import twitter_model from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, plot_autoscale +from openbb_terminal.helper_funcs import export_data, plot_autoscale, get_closing_price from openbb_terminal.rich_config import console logger = logging.getLogger(__name__) @@ -74,6 +74,7 @@ def display_sentiment( ticker: str, n_tweets: int, n_days_past: int, + compare: bool, export: str = "", external_axes: Optional[List[plt.Axes]] = None, ): @@ -87,6 +88,8 @@ def display_sentiment( Number of tweets to get per hour n_days_past: int Number of days to extract tweets for + compare: bool + Show corresponding change in stock price export: str Format to export tweet dataframe """ @@ -152,18 +155,34 @@ def display_sentiment( df_tweets = df_tweets.sort_values(by="date") df_tweets["cumulative_compound"] = df_tweets["sentiment"].cumsum() - # This plot has 2 axis - if external_axes is None: - _, axes = plt.subplots( - 2, 1, sharex=True, figsize=plot_autoscale(), dpi=cfg_plot.PLOT_DPI - ) - ax1, ax2 = axes + ax1, ax2, ax3 = None, None, None + + if compare: + # This plot has 3 axis + if external_axes is None: + _, axes = plt.subplots( + 3, 1, sharex=False, figsize=plot_autoscale(), dpi=cfg_plot.PLOT_DPI + ) + ax1, ax2, ax3 = axes + else: + if len(external_axes) != 3: + logger.error("Expected list of three axis item.") + console.print("[red]Expected list of three axis item./n[/red]") + return + (ax1, ax2, ax3) = external_axes else: - if len(external_axes) != 2: - logger.error("Expected list of one axis item.") - console.print("[red]Expected list of one axis item./n[/red]") - return - (ax1, ax2) = external_axes + # This plot has 2 axis + if external_axes is None: + _, axes = plt.subplots( + 2, 1, sharex=True, figsize=plot_autoscale(), dpi=cfg_plot.PLOT_DPI + ) + ax1, ax2 = axes + else: + if len(external_axes) != 2: + logger.error("Expected list of one axis item.") + console.print("[red]Expected list of one axis item./n[/red]") + return + (ax1, ax2) = external_axes ax1.plot( pd.to_datetime(df_tweets["created_at"]), @@ -199,6 +218,21 @@ def display_sentiment( ax2.set_ylabel("VADER Polarity Scores") theme.style_primary_axis(ax2) + if compare: + # get stock end price for each corresponding day if compare == True + closing_price_df = get_closing_price(ticker, n_days_past) + if ax3: + ax3.plot( + closing_price_df["Date"], + closing_price_df["Close"], + label=pd.to_datetime(closing_price_df["Date"]) + .iloc[0] + .strftime("%Y-%m-%d"), + ) + + ax3.set_ylabel("Stock Price") + theme.style_primary_axis(ax3) + if external_axes is None: theme.visualize_output() diff --git a/openbb_terminal/helper_funcs.py b/openbb_terminal/helper_funcs.py index 6e3a4b2bfa5..e3fb8ee67f2 100644 --- a/openbb_terminal/helper_funcs.py +++ b/openbb_terminal/helper_funcs.py @@ -5,7 +5,7 @@ import argparse import logging from pathlib import Path from typing import List, Tuple, Union -from datetime import datetime, timedelta +from datetime import datetime, timedelta, date as d import os import random import re @@ -24,6 +24,7 @@ from pandas.plotting import register_matplotlib_converters import pandas.io.formats.format import requests from screeninfo import get_monitors +import yfinance as yf from openbb_terminal.rich_config import console from openbb_terminal import feature_flags as obbff @@ -1398,6 +1399,33 @@ def handle_error_code(requests_obj, error_code_map): console.print(error_msg) +def get_closing_price(ticker, days): + + """Get historical close price for n days in past for market asset + + Parameters + ---------- + ticker : str + Ticker to get data for + days : datetime + No. of days in past + + Returns + ---------- + data : pd.DataFrame + Historic close prices for ticker for given days + """ + tick = yf.Ticker(ticker) + df = tick.history( + start=d.today() - timedelta(days=days), + interval="1d", + )["Close"] + df = df.to_frame().reset_index() + df = df.rename(columns={0: "Close"}) + df.index.name = "index" + return df + + def camel_case_split(string: str) -> str: """Converts a camel case string to separate words diff --git a/openbb_terminal/stocks/behavioural_analysis/ba_controller.py b/openbb_terminal/stocks/behavioural_analysis/ba_controller.py index be1600818fc..ea04db094d5 100644 --- a/openbb_terminal/stocks/behavioural_analysis/ba_controller.py +++ b/openbb_terminal/stocks/behavioural_analysis/ba_controller.py @@ -817,6 +817,13 @@ class BehaviouralAnalysisController(StockBaseController): default=6, help="number of days in the past to extract tweets.", ) + parser.add_argument( + "-c", + "--compare", + action="store_true", + dest="compare", + help="show corresponding change in stock price", + ) if other_args and "-" not in other_args[0][0]: other_args.insert(0, "-l") ns_parser = parse_known_args_and_warn( @@ -828,6 +835,7 @@ class BehaviouralAnalysisController(StockBaseController): ticker=self.ticker, n_tweets=ns_parser.limit, n_days_past=ns_parser.n_days_past, + compare=ns_parser.compare, export=ns_parser.export, ) else: diff --git a/tests/openbb_terminal/stocks/behavioural_analysis/test_ba_controller.py b/tests/openbb_terminal/stocks/behavioural_analysis/test_ba_controller.py index 17675abcfcb..7d412dee433 100644 --- a/tests/openbb_terminal/stocks/behavioural_analysis/test_ba_controller.py +++ b/tests/openbb_terminal/stocks/behavioural_analysis/test_ba_controller.py @@ -325,13 +325,14 @@ def test_call_func_expect_queue(expected_queue, queue, func): ), ( "call_sentiment", - ["--limit=20", "--days=2", "--export=csv"], + ["--limit=20", "--days=2", "--compare", "--export=csv"], "twitter_view.display_sentiment", [], dict( ticker="MOCK_TICKER", n_tweets=20, n_days_past=2, + compare=True, export="csv", ), ),