dashboard / src /pages /chart.py
Arrechenash's picture
Support downloading data from chart
5e1672d
from datetime import datetime, timedelta
import pandas as pd
import plotly.graph_objs as go
import streamlit as st
import pytz
from datasource import get_stock_bars
def get_ny_today():
ny_tz = pytz.timezone("America/New_York")
return datetime.now(ny_tz).date()
st.set_page_config(layout="wide")
st.title("Candlestick Chart")
st.sidebar.title("Filters")
symbol = st.sidebar.text_input("Ticker symbol", value="TSLA").upper()
date_start = st.sidebar.date_input(
"Start date",
get_ny_today() - timedelta(days=1),
max_value=get_ny_today() - timedelta(days=1),
)
timeframe = st.sidebar.selectbox(
"Timeframe", options=["1m", "5m", "15m", "30m", "1h", "1d"], index=2
)
try:
if timeframe == "1d":
actual_start_date = date_start - timedelta(days=29) # 29 + 1 = 30 days total
actual_end_date = date_start + timedelta(days=1)
else:
actual_start_date = date_start
actual_end_date = date_start + timedelta(days=1)
bars = get_stock_bars(
symbol, actual_start_date, actual_end_date, interval=timeframe
)
if bars.empty:
st.warning("No data. Check symbol and dates.")
else:
bars = bars.reset_index()
bars["timestamp"] = bars["timestamp"].dt.tz_convert("America/New_York")
bars = bars.set_index("timestamp")
bars["volume"] = pd.to_numeric(bars["volume"], errors="coerce").fillna(0)
typical_price = (bars["high"] + bars["low"] + bars["close"]) / 3.0
bars["vwap"] = (typical_price * bars["volume"]).cumsum() / bars[
"volume"
].cumsum()
if timeframe != "1d":
premarket_mask = bars.index.time < pd.to_datetime("09:30:00").time()
premarket_high = (
bars.loc[premarket_mask, "high"].max() if premarket_mask.any() else None
)
else:
premarket_high = None
timestamps = [ts.strftime("%Y-%m-%d %H:%M:%S") for ts in bars.index]
open_vals = bars["open"].tolist()
high_vals = bars["high"].tolist()
low_vals = bars["low"].tolist()
close_vals = bars["close"].tolist()
vwap_vals = bars["vwap"].tolist()
volume_vals = bars["volume"].tolist()
fig = go.Figure()
fig.add_trace(
go.Candlestick(
x=timestamps,
open=open_vals,
high=high_vals,
low=low_vals,
close=close_vals,
name="Candlestick",
)
)
fig.add_trace(
go.Scatter(
x=timestamps,
y=vwap_vals,
mode="lines",
line=dict(color="yellow", width=1),
name="VWAP",
)
)
if premarket_high is not None and pd.notna(premarket_high):
fig.add_trace(
go.Scatter(
x=[timestamps, timestamps[-1]],
y=[premarket_high, premarket_high],
mode="lines",
line=dict(color="red", width=1, dash="dash"),
name="Premarket High",
)
)
fig.add_trace(
go.Bar(
x=timestamps,
y=volume_vals,
yaxis="y2",
marker=dict(color="rgba(200,200,200,0.5)"),
name="Volume",
opacity=0.5,
)
)
if timeframe != "1d":
bars_dates = pd.to_datetime(bars.index.date).unique()
for day in bars_dates:
dm = pd.Timestamp(day).strftime("%Y-%m-%d")
fig.add_vrect(
x0=f"{dm} 04:00:00",
x1=f"{dm} 09:30:00",
fillcolor="rgba(0, 200, 255, 0.10)",
layer="below",
line_width=0,
annotation_text="Pre-market",
annotation_position="top left",
)
fig.add_vrect(
x0=f"{dm} 16:00:00",
x1=f"{dm} 20:00:00",
fillcolor="rgba(255, 200, 0, 0.08)",
layer="below",
line_width=0,
annotation_text="After-hours",
annotation_position="top left",
)
if timeframe == "1d":
title = f"{symbol} - {timeframe} ({actual_start_date.strftime('%Y-%m-%d')} to {date_start.strftime('%Y-%m-%d')})"
else:
title = f"{symbol} - {timeframe}"
fig.update_layout(
title=title,
xaxis_title="Date/Time",
yaxis_title="Price",
xaxis_rangeslider_visible=False,
yaxis=dict(domain=[0.3, 1]),
yaxis2=dict(domain=[0, 0.25], title="Volume"),
legend=dict(orientation="h"),
margin=dict(t=40, b=20),
hovermode="x unified",
height=720,
)
st.plotly_chart(fig, use_container_width=True)
preview_cols = ["open", "high", "low", "close", "volume", "vwap"]
if premarket_high is not None and pd.notna(premarket_high):
preview_cols.append("premarket_high")
bars["premarket_high"] = premarket_high
st.write("Data:")
st.dataframe(bars, use_container_width=True)
except Exception as e:
st.error(f"Error fetching or plotting data: {e}")
import traceback
st.write("Full traceback:")
st.code(traceback.format_exc())