CatPtain's picture
Upload 225 files
a2afe2f verified
"""Views for the technical Extension."""
# pylint: disable=too-many-locals,use-dict-literal
from typing import TYPE_CHECKING, Any
from openbb_charting.core.to_chart import to_chart
from openbb_charting.styles.colors import LARGE_CYCLER
if TYPE_CHECKING:
from openbb_charting.core.openbb_figure import OpenBBFigure
class TechnicalViews:
"""Technical Views."""
@staticmethod
def technical_sma(**kwargs) -> tuple["OpenBBFigure", dict[str, Any]]:
"""Plot simple moving average chart."""
if "ma_type" not in kwargs:
kwargs["ma_type"] = "sma"
return _ta_ma(**kwargs)
@staticmethod
def technical_ema(**kwargs) -> tuple["OpenBBFigure", dict[str, Any]]:
"""Exponential moving average chart."""
if "ma_type" not in kwargs:
kwargs["ma_type"] = "ema"
return _ta_ma(**kwargs)
@staticmethod
def technical_hma(**kwargs) -> tuple["OpenBBFigure", dict[str, Any]]:
"""Hull moving average chart."""
if "ma_type" not in kwargs:
kwargs["ma_type"] = "hma"
return _ta_ma(**kwargs)
@staticmethod
def technical_wma(**kwargs) -> tuple["OpenBBFigure", dict[str, Any]]:
"""Weighted moving average chart."""
if "ma_type" not in kwargs:
kwargs["ma_type"] = "wma"
return _ta_ma(**kwargs)
@staticmethod
def technical_zlma(**kwargs) -> tuple["OpenBBFigure", dict[str, Any]]:
"""Zero lag moving average chart."""
if "ma_type" not in kwargs:
kwargs["ma_type"] = "zlma"
return _ta_ma(**kwargs)
@staticmethod
def technical_aroon(**kwargs) -> tuple["OpenBBFigure", dict[str, Any]]:
"""Technical Aroon Chart."""
# pylint: disable=import-outside-toplevel
from openbb_charting.core.plotly_ta.ta_class import PlotlyTA
from openbb_core.app.utils import basemodel_to_df
from pandas import DataFrame
if "data" in kwargs and isinstance(kwargs["data"], DataFrame):
data = kwargs["data"]
else:
data = basemodel_to_df(
kwargs["obbject_item"], index=kwargs.get("index", "date")
)
if "date" in data.columns:
data = data.set_index("date")
if "symbol" in data.columns and len(data.symbol.unique()) > 1:
raise ValueError(
"Please provide data with only one symbol and columns for OHLC."
)
symbol = kwargs.get("symbol", "")
volume = kwargs.get("volume") is True
title = f"Aroon Indicator & Oscillator {symbol}"
length = kwargs.get("length", 25)
scalar = kwargs.get("scalar", 100)
symbol = kwargs.get("symbol", "")
ta = PlotlyTA()
fig = ta.plot( # type: ignore
data,
dict(aroon=dict(length=length, scalar=scalar)),
title,
False,
volume=volume,
)
content = fig.show(external=True).to_plotly_json()
return fig, content
@staticmethod
def technical_macd(**kwargs) -> tuple["OpenBBFigure", dict[str, Any]]:
"""Plot moving average convergence divergence chart."""
# pylint: disable=import-outside-toplevel
from openbb_charting.core.plotly_ta.ta_class import PlotlyTA
from openbb_core.app.utils import basemodel_to_df
from pandas import DataFrame
if "data" in kwargs and isinstance(kwargs["data"], DataFrame):
data = kwargs["data"]
else:
data = basemodel_to_df(
kwargs["obbject_item"], index=kwargs.get("index", "date")
)
if "date" in data.columns:
data = data.set_index("date")
if "symbol" in data.columns and len(data.symbol.unique()) > 1:
raise ValueError(
"Please provide data with only one symbol and columns for OHLC."
)
fast = kwargs.get("fast", 12)
slow = kwargs.get("slow", 26)
signal = kwargs.get("signal", 9)
symbol = kwargs.get("symbol", "")
title = f"{symbol.upper()} MACD"
volume = kwargs.get("volume") is True
ta = PlotlyTA()
fig = ta.plot( # type: ignore
data,
dict(macd=dict(fast=fast, slow=slow, signal=signal)),
title,
False,
volume=volume,
)
content = fig.show(external=True).to_plotly_json()
return fig, content
@staticmethod
def technical_adx(**kwargs) -> tuple["OpenBBFigure", dict[str, Any]]:
"""Average directional movement index chart."""
# pylint: disable=import-outside-toplevel
from openbb_charting.core.plotly_ta.ta_class import PlotlyTA
from openbb_core.app.utils import basemodel_to_df
from pandas import DataFrame
if "data" in kwargs and isinstance(kwargs["data"], DataFrame):
data = kwargs["data"]
else:
data = basemodel_to_df(
kwargs["obbject_item"], index=kwargs.get("index", "date")
)
if "date" in data.columns:
data = data.set_index("date")
if "symbol" in data.columns and len(data.symbol.unique()) > 1:
raise ValueError(
"Please provide data with only one symbol and columns for OHLC."
)
length = kwargs.get("length", 14)
scalar = kwargs.get("scalar", 100.0)
drift = kwargs.get("drift", 1)
symbol = kwargs.get("symbol", "")
ta = PlotlyTA()
fig = ta.plot( # type: ignore
data,
dict(adx=dict(length=length, scalar=scalar, drift=drift)),
f"Average Directional Movement Index (ADX) {symbol}",
False,
volume=False,
)
content = fig.show(external=True).to_plotly_json()
return fig, content
@staticmethod
def technical_rsi(**kwargs) -> tuple["OpenBBFigure", dict[str, Any]]:
"""Relative strength index chart."""
# pylint: disable=import-outside-toplevel
from openbb_charting.core.plotly_ta.ta_class import PlotlyTA
from openbb_core.app.utils import basemodel_to_df
from pandas import DataFrame
if "data" in kwargs and isinstance(kwargs["data"], DataFrame):
data = kwargs["data"]
else:
data = basemodel_to_df(
kwargs["obbject_item"], index=kwargs.get("index", "date")
)
if "date" in data.columns:
data = data.set_index("date")
if "symbol" in data.columns and len(data.symbol.unique()) > 1:
raise ValueError(
"Please provide data with only one symbol and columns for OHLC."
)
window = kwargs.get("window", 14)
scalar = kwargs.get("scalar", 100.0)
drift = kwargs.get("drift", 1)
symbol = kwargs.get("symbol", "")
ta = PlotlyTA()
fig = ta.plot( # type: ignore
data,
dict(rsi=dict(length=window, scalar=scalar, drift=drift)),
f"{symbol.upper()} RSI {window}",
False,
volume=False,
)
content = fig.show(external=True).to_plotly_json()
return fig, content
@staticmethod
def technical_cones(**kwargs) -> tuple["OpenBBFigure", dict[str, Any]]:
"""Volatility Cones Chart."""
# pylint: disable=import-outside-toplevel
from openbb_charting.core.chart_style import ChartStyle
from openbb_charting.core.openbb_figure import OpenBBFigure
from openbb_core.app.utils import basemodel_to_df
from pandas import DataFrame
data = kwargs.get("data")
if isinstance(data, DataFrame) and not data.empty and "window" in data.columns:
df_ta = data.set_index("window")
else:
df_ta = basemodel_to_df(kwargs["obbject_item"], index="window") # type: ignore
df_ta.columns = [col.title().replace("_", " ") for col in df_ta.columns]
# Check if the data is formatted as expected.
if not all(
col in df_ta.columns for col in ["Realized", "Min", "Median", "Max"]
):
raise ValueError("Data supplied does not match the expected format.")
model = (
str(kwargs.get("model"))
.replace("std", "Standard Deviation")
.replace("_", "-")
.title()
if kwargs.get("model")
else "Standard Deviation"
)
symbol = str(kwargs.get("symbol")) + " - " if kwargs.get("symbol") else ""
title = (
str(kwargs.get("title"))
if kwargs.get("title")
else f"{symbol}Realized Volatility Cones - {model} Model"
)
colors = [
"green",
"red",
"burlywood",
"grey",
"orange",
"blue",
]
fig = OpenBBFigure()
fig.update_layout(ChartStyle().plotly_template.get("layout", {}))
text_color = "black" if ChartStyle().plt_style == "light" else "white"
for i, col in enumerate(df_ta.columns):
fig.add_scatter(
x=df_ta.index,
y=df_ta[col],
name=col,
mode="lines+markers",
hovertemplate=f"{col}: %{{y}}<extra></extra>",
marker=dict(
color=colors[i],
size=11,
),
)
fig.set_title(title)
fig.update_layout(
paper_bgcolor=(
"rgba(0,0,0,0)" if text_color == "white" else "rgba(255,255,255,0)"
),
plot_bgcolor=(
"rgba(0,0,0,0)" if text_color == "white" else "rgba(255,255,255,0)"
),
font=dict(color=text_color),
legend=dict(
orientation="h",
yanchor="bottom",
xanchor="right",
y=1.02,
x=1,
bgcolor=(
"rgba(0,0,0,0)" if text_color == "white" else "rgba(255,255,255,0)"
),
),
yaxis=dict(
ticklen=0,
showgrid=True,
showline=True,
mirror=True,
zeroline=False,
gridcolor="rgba(128,128,128,0.3)",
),
xaxis=dict(
type="category",
tickmode="array",
ticklen=0,
tickvals=df_ta.index,
ticktext=df_ta.index,
title_text="Period",
showgrid=False,
showline=True,
mirror=True,
zeroline=False,
),
margin=dict(l=20, r=20, b=20),
dragmode="pan",
)
content = fig.to_plotly_json()
return fig, content
@staticmethod
def technical_relative_rotation(
**kwargs: Any,
) -> tuple["OpenBBFigure", dict[str, Any]]:
"""Relative Rotation Chart."""
# pylint: disable=import-outside-toplevel
from openbb_charting.charts import relative_rotation # noqa
from openbb_charting.core.chart_style import ChartStyle # noqa
from openbb_charting.core.openbb_figure import OpenBBFigure # noqa
from openbb_core.app.utils import basemodel_to_df # noqa
ratios_df = basemodel_to_df(kwargs["obbject_item"].rs_ratios, index="date") # type: ignore
momentum_df = basemodel_to_df(kwargs["obbject_item"].rs_momentum, index="date") # type: ignore
benchmark_symbol = kwargs["obbject_item"].benchmark # type: ignore
study = kwargs.get("study")
study = str(kwargs["obbject_item"].study) if study is None else str(study)
show_tails = kwargs.get("show_tails")
show_tails = True if show_tails is None else show_tails
tail_periods = int(kwargs.get("tail_periods")) if "tail_periods" in kwargs else 16 # type: ignore
tail_interval = str(kwargs.get("tail_interval")) if "tail_interval" in kwargs else "week" # type: ignore
date = kwargs.get("date") if "date" in kwargs else None # type: ignore
show_tails = False if date is not None else show_tails
if ratios_df.empty or momentum_df.empty:
raise RuntimeError("Error: No data to plot.")
if show_tails is True:
fig = relative_rotation.create_rrg_with_tails(
ratios_df, momentum_df, study, benchmark_symbol, tail_periods, tail_interval # type: ignore
)
if show_tails is False:
fig = relative_rotation.create_rrg_without_tails(
ratios_df, momentum_df, benchmark_symbol, study, date # type: ignore
)
figure = OpenBBFigure(fig) # pylint: disable=E0606
font_color = "black" if ChartStyle().plt_style == "light" else "white"
figure.update_layout(
paper_bgcolor=(
"rgba(0,0,0,0)" if font_color == "white" else "rgba(255,255,255,255)"
),
plot_bgcolor="rgba(255,255,255,1)",
font=dict(color=font_color),
yaxis=dict(
showgrid=True,
gridcolor="rgba(128,128,128,0.3)",
side="left",
showline=True,
zeroline=True,
mirror=True,
ticklen=0,
tickfont=dict(size=14),
title=dict(font=dict(size=16)),
),
xaxis=dict(
showgrid=True,
gridcolor="rgba(128,128,128,0.3)",
showline=True,
zeroline=True,
mirror=True,
ticklen=0,
tickfont=dict(size=14),
title=dict(font=dict(size=16)),
hoverformat="",
),
hoverlabel=dict(
font_size=12,
),
)
if kwargs.get("title") is not None:
figure.set_title(str(kwargs.get("title")))
content = figure.to_plotly_json()
return figure, content
def _ta_ma(**kwargs):
"""Plot moving average helper."""
# pylint: disable=import-outside-toplevel
from openbb_charting.core.chart_style import ChartStyle
from openbb_charting.core.openbb_figure import OpenBBFigure
from openbb_core.app.utils import basemodel_to_df
from pandas import DataFrame
index = (
kwargs.get("index")
if "index" in kwargs and kwargs.get("index") is not None
else "date"
)
data = kwargs.get("data")
ma_type = (
kwargs["ma_type"]
if "ma_type" in kwargs and kwargs.get("ma_type") is not None
else "sma"
)
ma_types = ma_type.split(",") if isinstance(ma_type, str) else ma_type
if isinstance(data, DataFrame) and not data.empty:
data = data.set_index(index) if index in data.columns else data
if data is None:
data = basemodel_to_df(kwargs["obbject_item"], index=index)
if isinstance(data, list):
data = basemodel_to_df(data, index=index)
window = (
kwargs.get("length", [])
if "length" in kwargs and kwargs.get("length") is not None
else [50]
)
offset = kwargs.get("offset", 0)
target = (
kwargs.get("target")
if "target" in kwargs and kwargs.get("target") is not None
else "close"
)
if target not in data.columns and "close" in data.columns:
target = "close"
if target not in data.columns and "close" not in data.columns:
raise ValueError(f"Column '{target}', or 'close', not found in the data.")
df = data.copy()
if target in data.columns:
df = df[[target]]
df.columns = ["close"]
title = (
kwargs.get("title")
if "title" in kwargs and kwargs.get("title") is not None
else f"{ma_type.upper()}"
)
fig = OpenBBFigure()
fig = fig.create_subplots(
1,
1,
shared_xaxes=True,
vertical_spacing=0.06,
horizontal_spacing=0.01,
row_width=[1],
specs=[[{"secondary_y": True}]],
)
fig.update_layout(ChartStyle().plotly_template.get("layout", {}))
font_color = "black" if ChartStyle().plt_style == "light" else "white"
ma_df = DataFrame()
window = [window] if isinstance(window, int) else window
for w in window:
for ma_type in ma_types:
ma_df[f"{ma_type.upper()} {w}"] = getattr(df.ta, ma_type)(
length=w, offset=offset
)
if kwargs.get("dropnan") is True:
ma_df = ma_df.dropna()
data = data.iloc[-len(ma_df) :]
if (
"candles" in kwargs
and kwargs.get("candles") is True
and kwargs.get("target") is None
):
volume = kwargs.get("volume") is True
fig, _ = to_chart(data, candles=True, volume=volume)
else:
ma_df[f"{target}".title()] = data[target]
for i, col in enumerate(ma_df.columns):
name = col.replace("_", " ")
fig.add_scatter(
x=ma_df.index,
y=ma_df[col],
name=name,
mode="lines",
hovertemplate=f"{name}: %{{y}}<extra></extra>",
line=dict(width=1, color=LARGE_CYCLER[i]),
showlegend=True,
)
fig.update_layout(
title=dict(text=title, x=0.5, font=dict(size=16)),
paper_bgcolor=(
"rgba(0,0,0,0)" if font_color == "white" else "rgba(255,255,255,255)"
),
plot_bgcolor=(
"rgba(0,0,0,0)" if font_color == "white" else "rgba(255,255,255,0)"
),
showlegend=True,
legend=dict(
orientation="h",
yanchor="bottom",
xanchor="right",
y=1.02,
x=0.95,
bgcolor="rgba(0,0,0,0)" if font_color == "white" else "rgba(255,255,255,0)",
),
xaxis=dict(
ticklen=0,
showgrid=True,
gridcolor="rgba(128,128,128,0.3)",
zeroline=True,
mirror=True,
),
yaxis=dict(
ticklen=0,
showgrid=True,
gridcolor="rgba(128,128,128,0.3)",
zeroline=True,
mirror=True,
autorange=True,
),
font=dict(color=font_color),
)
content = fig.show(external=True).to_plotly_json()
return fig, content