Yahoo Finance Dashboard Using einteract

2 min read

import datetime
import pandas as pd
import numpy as np
import yfinance as yf
import matplotlib.pyplot as plt
import ipywidgets as widgets
import einteract as ei
import traitlets
def rsi(series: pd.Series, window: int = 14) -> pd.Series:
delta = series.diff()
up = delta.clip(lower=0)
down = -delta.clip(upper=0)
ma_up = up.rolling(window).mean()
ma_down = down.rolling(window).mean()
rs = ma_up / (ma_down + 1e-12)
return 100 - (100 / (1 + rs))
class FinanceDashboard(ei.InteractBase):
def _interactive_params(self):
today = datetime.date.today()
default_start = today - datetime.timedelta(days=365)
return dict(
ticker = 'AAPL',
start = widgets.DatePicker(value=default_start, description='Start:'),
end = widgets.DatePicker(value=today, description='End:'),
load = widgets.Button(description='Fetch Data', button_style='primary'),
show_sma = True,
sma_window = (1,50),
show_ema = False,
ema_span = (1,50),
show_rsi = True,
plot = widgets.Button(description='Plot Data', button_style='primary')
)
def __init__(self, *args,**kwargs):
self.df = None # initial
super().__init__(*args, **kwargs)
self.relayout(
left_sidebar=self.groups.controls,
center = self.groups.outputs, pane_widths=[1,2,0],height='400px'
)
self.set_css({
"grid-gap": "8px",
"button": {"margin-left": "90px"},
"> *":{
"border-radius": "8px",
"background":"whitesmoke",
"padding": "8px",
}
})
@ei.callback
def fetch_history(self, ticker, start, end, load):
"""
Fetch historical daily OHLCV using yfinance.
start and end are 'YYYY-MM-DD' strings.
"""
if not ticker or ticker.strip() == "":
self.df = pd.DataFrame()
df = yf.download(ticker.strip(), start=start, end=end, progress=False, auto_adjust=True)
if not df.empty:
# ensure datetime index
df.index = pd.to_datetime(df.index)
self.df = df
self.params.load.button_style = "primary"
self.params.plot.button_style = "danger"
@ei.callback
def _wran_load(self, ticker, start, end):
self.params.load.button_style = "danger"
@ei.callback
def _wran_plot(self, ticker, show_sma, sma_window, show_ema, ema_span, show_rsi):
self.params.plot.button_style = "danger"
@ei.callback
def plot_stock(self, ticker,
show_sma, sma_window,
show_ema, ema_span, show_rsi, plot
):
self.params.plot.button_style = "primary"
df = self.df
if df is None or df.empty:
print(f"No data for '{ticker}'. Try another ticker or wider date range.")
return
price = df['Close']
fig, axes = plt.subplots(2 if show_rsi else 1, 1, sharex=True, figsize=(6.5,3.8),
gridspec_kw={'height_ratios': [3, 1]} if show_rsi else None)
if not isinstance(axes, (list, np.ndarray)):
axes = [axes]
ax_price = axes[0]
ax_price.plot(df.index, price, label='Close', linewidth=1.25)
ax_price.set_ylabel('Price (adjusted)')
ax_price.set_title(f"{ticker.upper()} — {df.index.min().date()} to {df.index.max().date()}")
if show_sma:
s = price.rolling(sma_window, min_periods=1).mean()
ax_price.plot(df.index, s, label=f"SMA{(sma_window)}", linestyle='--', linewidth=1)
if show_ema:
e = price.ewm(span=ema_span, adjust=False).mean()
ax_price.plot(df.index, e, label=f"EMA{ema_span}", linestyle=':', linewidth=1)
ax_price.legend(loc='upper left')
ax_price.grid(alpha=0.3)
if show_rsi:
ax_rsi = axes[1]
r = rsi(price)
ax_rsi.plot(df.index, r, label='RSI(14)')
ax_rsi.axhline(70, linestyle='--', alpha=0.6)
ax_rsi.axhline(30, linestyle='--', alpha=0.6)
ax_rsi.set_ylabel('RSI')
ax_rsi.set_ylim(0, 100)
ax_rsi.grid(alpha=0.3)
plt.tight_layout()
fd = FinanceDashboard()
fd
Above code generates the following dashboard. It logically shows red buttons to load data or update plot if inputs change.
10
Subscribe to my newsletter
Read articles from Abdul Saboor directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
