Skip to content

Commit

Permalink
Adds a matlab like hold on to plot on same axis (#5173)
Browse files Browse the repository at this point in the history
* definitely not juan walking me through everything

* trying to pretty it

* start doing same axis with hold

* actually do the sameaxis correctly

* Better docstring

* Fix mypy

* mypy3

* pylint fixes

* Add message about candle

* might work, might now

* Update stocks_controller.py

* Update plotly_helper.py

* Update plotly_helper.py

* Make fmp view legends nice

* Update helper_funcs.py

* Add custom legends for when hold on

* bigger font

* add colors to new axes

* add custom title, remove location

* Start guide.

* Update plot-holds.md

Add figures

* More words

* Update plot-holds.md

* Finish guide

* tests

* Sneak in test updates for release

* guess not sneak test

---------

Co-authored-by: teh_coderer <[email protected]>
  • Loading branch information
jmaslek and tehcoderer authored Jul 19, 2023
1 parent 1bfc419 commit 8b00778
Show file tree
Hide file tree
Showing 12 changed files with 370 additions and 42 deletions.
64 changes: 63 additions & 1 deletion openbb_terminal/config_terminal.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# IMPORTATION STANDARD
import copy
from pathlib import Path

# IMPORTATION THIRDPARTY
from typing import Optional
from typing import TYPE_CHECKING, Optional, TypeVar

import i18n

Expand All @@ -19,6 +20,67 @@

from .helper_classes import TerminalStyle as _TerminalStyle

if TYPE_CHECKING:
from openbb_terminal import OpenBBFigure


OpenBBFigureT = TypeVar("OpenBBFigureT", bound="OpenBBFigure")
HOLD: bool = False
COMMAND_ON_CHART: bool = True
current_figure: Optional[OpenBBFigureT] = None # type: ignore
new_axis: bool = True
legends = []
last_legend = ""


# pylint: disable=global-statement
def set_last_legend(leg: str):
global last_legend
last_legend = copy.deepcopy(leg)


def get_last_legend() -> str:
return copy.deepcopy(last_legend)


def append_legend(append_leg: str):
global legends
legends.append(append_leg)
legends = copy.deepcopy(legends)


def reset_legend():
global legends
legends = []


def get_legends() -> list:
return legends


def set_same_axis() -> None:
global new_axis
new_axis = False


def set_new_axis() -> None:
global new_axis
new_axis = True


def make_new_axis() -> bool:
return new_axis


def get_current_figure() -> Optional["OpenBBFigure"]:
return current_figure


def set_current_figure(fig: Optional[OpenBBFigureT] = None):
# pylint: disable=global-statement
global current_figure
current_figure = fig


def start_plot_backend():
"""Starts the plot backend"""
Expand Down
1 change: 1 addition & 0 deletions openbb_terminal/core/completer/choices.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ def build_controller_choice_map(controller) -> dict:
controller_choice_map: dict = {c: {} for c in controller.controller_choices}
controller_choice_map["support"] = controller.SUPPORT_CHOICES
controller_choice_map["about"] = controller.ABOUT_CHOICES
controller_choice_map["hold"] = controller.HELP_CHOICES

for command in command_list:
try:
Expand Down
7 changes: 5 additions & 2 deletions openbb_terminal/core/plots/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class PyWry(DummyBackend): # type: ignore

from svglib.svglib import svg2rlg

from openbb_terminal import config_terminal
from openbb_terminal.base_helpers import console
from openbb_terminal.core.session.current_system import get_current_system
from openbb_terminal.core.session.current_user import get_current_user
Expand Down Expand Up @@ -201,8 +202,10 @@ def send_figure(

json_data = json.loads(fig.to_json())

json_data.update(self.get_json_update(command_location))

if config_terminal.COMMAND_ON_CHART:
json_data.update(self.get_json_update(command_location))
else:
json_data.update(self.get_json_update(" "))
outgoing = dict(
html=self.get_plotly_html(),
json_data=json_data,
Expand Down
59 changes: 55 additions & 4 deletions openbb_terminal/core/plots/plotly_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from plotly.subplots import make_subplots
from scipy import stats

from openbb_terminal import config_terminal
from openbb_terminal.base_helpers import console, strtobool
from openbb_terminal.core.config.paths import (
STYLES_DIRECTORY_REPO,
Expand Down Expand Up @@ -317,6 +318,9 @@ class OpenBBFigure(go.Figure):

def __init__(self, fig: Optional[go.Figure] = None, **kwargs) -> None:
super().__init__()
if fig is None and config_terminal.current_figure and config_terminal.HOLD:
fig = config_terminal.current_figure

if fig:
self.__dict__ = fig.__dict__

Expand All @@ -334,6 +338,16 @@ def __init__(self, fig: Optional[go.Figure] = None, **kwargs) -> None:

self._subplot_xdates: Dict[int, Dict[int, List[Any]]] = {}

fig = config_terminal.get_current_figure()
if fig is None and config_terminal.HOLD:
config_terminal.append_legend(config_terminal.last_legend)
if fig is not None:
traces = len(fig.data)
self.update_layout(
{f"yaxis{traces+1}": dict(title=kwargs.pop("yaxis_title", ""))}
)
config_terminal.append_legend(config_terminal.last_legend)

if xaxis := kwargs.pop("xaxis", None):
self.update_xaxes(xaxis)
if yaxis := kwargs.pop("yaxis", None):
Expand All @@ -348,6 +362,34 @@ def __init__(self, fig: Optional[go.Figure] = None, **kwargs) -> None:
width=plots_backend().WIDTH,
)

def set_secondary_axis(
self, title: str, row: Optional[int] = None, col: Optional[int] = None, **kwargs
) -> "OpenBBFigure":
"""Set secondary axis.
Parameters
----------
title : str
Title of the axis
row : int, optional
Row of the axis, by default None
col : int, optional
Column of the axis, by default None
**kwargs
Keyword arguments to pass to go.Figure.update_layout
"""
axis = "yaxis"
title = kwargs.pop("title", "")
if (fig := config_terminal.get_current_figure()) is not None:
total_axes = max(2, len(list(fig.select_yaxes())))
axis = f"yaxis{total_axes+1}"
if config_terminal.make_new_axis():
kwargs["side"] = "left"
kwargs.pop("secondary_y", None)
return self.update_layout(**{axis: dict(title=title, **kwargs)})

return self.update_yaxes(title=title, row=row, col=col, **kwargs)

@property
def subplots_kwargs(self):
"""Get subplots kwargs property."""
Expand Down Expand Up @@ -654,7 +696,7 @@ def _validate_x(data: Union[np.ndarray, pd.Series, type[TimeSeriesT]]):
col=col,
)

max_y = max(max_y, max(y * 2))
max_y = max(max_y, *(y * 2))

self.update_yaxes(
position=0.0,
Expand Down Expand Up @@ -753,7 +795,7 @@ def set_yaxis_title(
col : `int`, optional
Column number, by default None
"""
self.update_yaxes(title=title, row=row, col=col, **kwargs)
self.set_secondary_axis(title=title, row=row, col=col, **kwargs)
return self

def add_hline_legend(
Expand Down Expand Up @@ -1108,8 +1150,17 @@ def show(
# This is done to avoid opening after exporting
if export_image:
self._exported = True

# We send the figure to the backend to be displayed
if config_terminal.HOLD:
# pylint: disable=import-outside-toplevel
from openbb_terminal.helper_funcs import command_location

for trace in self.select_traces():
if trace.name and "/" in trace.name:
continue
trace.name = f"{trace.name} {command_location}"
config_terminal.set_current_figure(self)
# We send the figure to the backend to be displayed
return None
return plots_backend().send_figure(self, export_image)
except Exception:
# If the backend fails, we just show the figure normally
Expand Down
10 changes: 7 additions & 3 deletions openbb_terminal/helper_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ def set_command_location(cmd_loc: str):
cmd_loc: str
Command location called by user
"""
if cmd_loc.split("/")[-1] == "hold":
return
global command_location # noqa
command_location = cmd_loc

Expand Down Expand Up @@ -1521,8 +1523,6 @@ def export_data(
margin : bool
Automatically adjust subplot parameters to give specified padding.
"""
if not figure:
figure = OpenBBFigure()

if export_type:
saved_path = compose_export_path(func_name, dir_path).resolve()
Expand Down Expand Up @@ -1601,14 +1601,18 @@ def export_data(
writer, sheet_name=sheet_name, index=True, header=True
)
elif saved_path.suffix in [".jpg", ".pdf", ".png", ".svg"]:
if figure is None:
console.print("No plot to export.")
continue
figure.show(export_image=saved_path, margin=margin)
else:
console.print("Wrong export file specified.")
continue

console.print(f"Saved file: {saved_path}")

figure._exported = True # pylint: disable=protected-access
if figure is not None:
figure._exported = True # pylint: disable=protected-access


def get_rf() -> float:
Expand Down
Loading

0 comments on commit 8b00778

Please sign in to comment.