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())