From 6c6fcd312c2db0f6375f00aff2578a14520b88b8 Mon Sep 17 00:00:00 2001 From: "didierlopes.eth" Date: Mon, 28 Feb 2022 11:39:08 +0000 Subject: [PATCH] Line custom markers to highlight events of interest (#1423) * add gst letters only * highlight events on qa/line * update hugo * add number to markers --- .../common/quantitative_analysis/qa_view.py | 39 +++++++++++++++++++ .../quantitative_analysis/qa_controller.py | 19 ++++++++- gamestonk_terminal/helper_funcs.py | 24 ++++++++++++ .../quantitative_analysis/qa_controller.py | 19 ++++++++- .../quantitative_analysis/line/_index.md | 8 ++-- 5 files changed, 104 insertions(+), 5 deletions(-) diff --git a/gamestonk_terminal/common/quantitative_analysis/qa_view.py b/gamestonk_terminal/common/quantitative_analysis/qa_view.py index b5d34ba80ca..c2a6170a6db 100644 --- a/gamestonk_terminal/common/quantitative_analysis/qa_view.py +++ b/gamestonk_terminal/common/quantitative_analysis/qa_view.py @@ -13,6 +13,7 @@ from typing import Any, Optional, List import matplotlib import matplotlib.patches as mpatches import matplotlib.pyplot as plt +import matplotlib.dates as mdates import numpy as np import pandas as pd import seaborn as sns @@ -896,6 +897,8 @@ def display_line( title: str = "", log_y: bool = True, draw: bool = False, + markers_lines: Optional[List[datetime]] = None, + markers_scatter: Optional[List[datetime]] = None, export: str = "", external_axes: Optional[List[plt.Axes]] = None, ): @@ -911,6 +914,10 @@ def display_line( Flag for showing y on log scale draw: bool Flag for drawing lines and annotating on the plot + markers_lines: Optional[List[datetime]] + List of dates to highlight using vertical lines + markers_scatter: Optional[List[datetime]] + List of dates to highlight using scatter export: str Format to export data external_axes : Optional[List[plt.Axes]], optional @@ -939,6 +946,38 @@ def display_line( else: ax.plot(data.index, data.values) + + if markers_lines: + ymin, ymax = ax.get_ylim() + ax.vlines(markers_lines, ymin, ymax, color="#00AAFF") + + if markers_scatter: + for n, marker_date in enumerate(markers_scatter): + price_location_idx = data.index.get_loc(marker_date, method="nearest") + # algo to improve text placement of highlight event number + if ( + 0 < price_location_idx < (len(data) - 1) + and data.iloc[price_location_idx - 1] + > data.iloc[price_location_idx] + and data.iloc[price_location_idx + 1] + > data.iloc[price_location_idx] + ): + text_loc = (0, -20) + else: + text_loc = (0, 10) + ax.annotate( + str(n), + (mdates.date2num(marker_date), data.iloc[price_location_idx]), + xytext=text_loc, + textcoords="offset points", + ) + ax.scatter( + marker_date, + data.iloc[price_location_idx], + color="#00AAFF", + s=200, + ) + ax.set_xlim(data.index[0], data.index[-1]) if title: diff --git a/gamestonk_terminal/custom/quantitative_analysis/qa_controller.py b/gamestonk_terminal/custom/quantitative_analysis/qa_controller.py index 1c8a9e252d4..406a83536c7 100644 --- a/gamestonk_terminal/custom/quantitative_analysis/qa_controller.py +++ b/gamestonk_terminal/custom/quantitative_analysis/qa_controller.py @@ -19,6 +19,7 @@ from gamestonk_terminal.helper_funcs import ( check_positive, check_proportion_range, parse_known_args_and_warn, + check_list_dates, ) from gamestonk_terminal.menu import session from gamestonk_terminal.parent_classes import BaseController @@ -248,7 +249,7 @@ class QaController(BaseController): formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False, prog="line", - description="Show line plot of selected data", + description="Show line plot of selected data and allow to draw lines or highlight specific datetimes.", ) parser.add_argument( "--log", @@ -265,6 +266,20 @@ class QaController(BaseController): action="store_true", default=False, ) + parser.add_argument( + "--ml", + help="Draw vertical line markers to highlight certain events", + dest="ml", + type=check_list_dates, + default="", + ) + parser.add_argument( + "--ms", + help="Draw scatter markers to highlight certain events", + dest="ms", + type=check_list_dates, + default="", + ) ns_parser = parse_known_args_and_warn( parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED @@ -275,6 +290,8 @@ class QaController(BaseController): title=f"{self.ticker} {self.target}", log_y=ns_parser.log, draw=ns_parser.draw, + markers_lines=ns_parser.ml, + markers_scatter=ns_parser.ms, ) @log_start_end(log=logger) diff --git a/gamestonk_terminal/helper_funcs.py b/gamestonk_terminal/helper_funcs.py index e89f4084618..73ce5ec8551 100644 --- a/gamestonk_terminal/helper_funcs.py +++ b/gamestonk_terminal/helper_funcs.py @@ -277,6 +277,30 @@ def valid_date_in_past(s: str) -> datetime: raise argparse.ArgumentTypeError(f"Not a valid date: {s}") from value_error +def check_list_dates(str_dates: str) -> List[datetime]: + """Argparse type to check list of dates provided have a valid format + + Parameters + ---------- + str_dates: str + string with dates separated by "," + + Returns + ------- + list_dates: List[datetime] + List of valid dates + """ + list_dates = list() + if str_dates: + if "," in str_dates: + for dt_marker in str_dates.split(","): + list_dates.append(valid_date(dt_marker)) + else: + list_dates.append(valid_date(str_dates)) + + return list_dates + + def valid_date(s: str) -> datetime: """Argparse type to check date is in valid format""" try: diff --git a/gamestonk_terminal/stocks/quantitative_analysis/qa_controller.py b/gamestonk_terminal/stocks/quantitative_analysis/qa_controller.py index 70b5277b94a..ed15ffa8cfd 100644 --- a/gamestonk_terminal/stocks/quantitative_analysis/qa_controller.py +++ b/gamestonk_terminal/stocks/quantitative_analysis/qa_controller.py @@ -19,6 +19,7 @@ from gamestonk_terminal.helper_funcs import ( check_positive, check_proportion_range, parse_known_args_and_warn, + check_list_dates, ) from gamestonk_terminal.menu import session from gamestonk_terminal.parent_classes import StockBaseController @@ -240,7 +241,7 @@ class QaController(StockBaseController): formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False, prog="line", - description="Show line plot of selected data", + description="Show line plot of selected data and allow to draw lines or highlight specific datetimes.", ) parser.add_argument( "--log", @@ -257,6 +258,20 @@ class QaController(StockBaseController): action="store_true", default=False, ) + parser.add_argument( + "--ml", + help="Draw vertical line markers to highlight certain events", + dest="ml", + type=check_list_dates, + default="", + ) + parser.add_argument( + "--ms", + help="Draw scatter markers to highlight certain events", + dest="ms", + type=check_list_dates, + default="", + ) ns_parser = parse_known_args_and_warn( parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED @@ -267,6 +282,8 @@ class QaController(StockBaseController): title=f"{self.ticker} {self.target}", log_y=ns_parser.log, draw=ns_parser.draw, + markers_lines=ns_parser.ml, + markers_scatter=ns_parser.ms, ) @log_start_end(log=logger) diff --git a/website/content/common/quantitative_analysis/line/_index.md b/website/content/common/quantitative_analysis/line/_index.md index 3762bdc2344..f3a9126b7da 100644 --- a/website/content/common/quantitative_analysis/line/_index.md +++ b/website/content/common/quantitative_analysis/line/_index.md @@ -1,16 +1,18 @@ ``` -usage: line [--log] [-h] [--export {png,jpg,pdf,svg}] +usage: line [--log] [-d] [--ml ML] [--ms MS] [-h] [--export {png,jpg,pdf,svg}] ``` -Show line plot of selected data. +Show line plot of selected data and allow to draw lines or highlight specific datetimes. ``` optional arguments: --log Plot with y on log scale (default: False) + -d, --draw Draw lines and annotate on the plot (default: False) + --ml ML Draw vertical line markers to highlight certain events (default: ) + --ms MS Draw scatter markers to highlight certain events (default: ) -h, --help show this help message (default: False) --export {png,jpg,pdf,svg} Export figure into png, jpg, pdf, svg (default: ) ``` ![line](https://user-images.githubusercontent.com/46355364/154307397-9c2e9325-bce6-494d-994f-a6d7db798798.png) -