Spaces:
Sleeping
Sleeping
Create daily.py
Browse files- app/daily.py +99 -0
app/daily.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# daily.py
|
| 2 |
+
import yfinance as yf
|
| 3 |
+
import pandas as pd
|
| 4 |
+
from datetime import datetime as dt
|
| 5 |
+
from plotly import graph_objs as go
|
| 6 |
+
from plotly.subplots import make_subplots
|
| 7 |
+
import traceback
|
| 8 |
+
|
| 9 |
+
from . import persist
|
| 10 |
+
from . import backblaze as b2
|
| 11 |
+
from .common import wrap_html, format_large_number
|
| 12 |
+
|
| 13 |
+
def fetch_daily(symbol, date_end, date_start, b2_save=False):
|
| 14 |
+
"""
|
| 15 |
+
Fetch daily historical stock data and generate full analysis dashboard with Plotly charts.
|
| 16 |
+
"""
|
| 17 |
+
key = f"daily_{symbol}"
|
| 18 |
+
if persist.exists(key, "html"):
|
| 19 |
+
cached = persist.load(key, "html")
|
| 20 |
+
if cached:
|
| 21 |
+
print(f"[{date_end}] Using cached daily for {symbol}")
|
| 22 |
+
return cached
|
| 23 |
+
|
| 24 |
+
try:
|
| 25 |
+
start = dt.strptime(date_start, "%d-%m-%Y").strftime("%Y-%m-%d")
|
| 26 |
+
end = dt.strptime(date_end, "%d-%m-%Y").strftime("%Y-%m-%d")
|
| 27 |
+
|
| 28 |
+
print(f"[{dt.now().strftime('%Y-%m-%d %H:%M:%S')}] Fetching daily for {symbol}")
|
| 29 |
+
df = yf.download(symbol + ".NS", start=start, end=end)
|
| 30 |
+
if df.empty:
|
| 31 |
+
return wrap_html(f"<h1>No daily data for {symbol}</h1>")
|
| 32 |
+
|
| 33 |
+
# Reset index and format Date
|
| 34 |
+
df.reset_index(inplace=True)
|
| 35 |
+
df["Date"] = df["Date"].dt.strftime("%d-%b-%Y")
|
| 36 |
+
|
| 37 |
+
# Optional save to Backblaze
|
| 38 |
+
if b2_save:
|
| 39 |
+
b2.upload_file("eshanhf", f"daily/{symbol}.csv", df)
|
| 40 |
+
|
| 41 |
+
# Calculate additional metrics
|
| 42 |
+
df["Daily Return %"] = df["Close"].pct_change() * 100
|
| 43 |
+
df["SMA20"] = df["Close"].rolling(20).mean()
|
| 44 |
+
df["SMA50"] = df["Close"].rolling(50).mean()
|
| 45 |
+
df["EMA20"] = df["Close"].ewm(span=20, adjust=False).mean()
|
| 46 |
+
df["UpperBB"] = df["Close"].rolling(20).mean() + 2*df["Close"].rolling(20).std()
|
| 47 |
+
df["LowerBB"] = df["Close"].rolling(20).mean() - 2*df["Close"].rolling(20).std()
|
| 48 |
+
|
| 49 |
+
# Summary stats
|
| 50 |
+
summary = pd.DataFrame({
|
| 51 |
+
"Metric": ["Start Date","End Date","Min Price","Max Price","Mean Price","Total Volume"],
|
| 52 |
+
"Value": [df["Date"].iloc[0], df["Date"].iloc[-1],
|
| 53 |
+
format_large_number(df["Close"].min()),
|
| 54 |
+
format_large_number(df["Close"].max()),
|
| 55 |
+
format_large_number(df["Close"].mean()),
|
| 56 |
+
format_large_number(df["Volume"].sum())]
|
| 57 |
+
})
|
| 58 |
+
|
| 59 |
+
# Plotly figure
|
| 60 |
+
fig = make_subplots(rows=2, cols=1, shared_xaxes=True,
|
| 61 |
+
vertical_spacing=0.1,
|
| 62 |
+
row_heights=[0.7,0.3],
|
| 63 |
+
specs=[[{"secondary_y": False}], [{"secondary_y": False}]])
|
| 64 |
+
|
| 65 |
+
# Candlestick
|
| 66 |
+
fig.add_trace(go.Candlestick(
|
| 67 |
+
x=df["Date"], open=df["Open"], high=df["High"], low=df["Low"], close=df["Close"],
|
| 68 |
+
name="OHLC"), row=1, col=1)
|
| 69 |
+
|
| 70 |
+
# SMA/EMA overlays
|
| 71 |
+
fig.add_trace(go.Scatter(x=df["Date"], y=df["SMA20"], mode="lines", name="SMA20"), row=1, col=1)
|
| 72 |
+
fig.add_trace(go.Scatter(x=df["Date"], y=df["SMA50"], mode="lines", name="SMA50"), row=1, col=1)
|
| 73 |
+
fig.add_trace(go.Scatter(x=df["Date"], y=df["EMA20"], mode="lines", name="EMA20"), row=1, col=1)
|
| 74 |
+
|
| 75 |
+
# Bollinger Bands
|
| 76 |
+
fig.add_trace(go.Scatter(x=df["Date"], y=df["UpperBB"], mode="lines", name="UpperBB", line=dict(dash="dot")), row=1, col=1)
|
| 77 |
+
fig.add_trace(go.Scatter(x=df["Date"], y=df["LowerBB"], mode="lines", name="LowerBB", line=dict(dash="dot")), row=1, col=1)
|
| 78 |
+
|
| 79 |
+
# Volume bar chart
|
| 80 |
+
fig.add_trace(go.Bar(x=df["Date"], y=df["Volume"], name="Volume"), row=2, col=1)
|
| 81 |
+
|
| 82 |
+
fig.update_layout(height=700, width=1000, title=f"{symbol} Daily Analysis",
|
| 83 |
+
xaxis_rangeslider_visible=False)
|
| 84 |
+
|
| 85 |
+
chart_html = fig.to_html(full_html=False, include_plotlyjs='cdn')
|
| 86 |
+
|
| 87 |
+
# Build table HTML
|
| 88 |
+
table_html = wrap_html(f"<h2>Summary</h2>{summary.to_html(index=False, escape=False)}")
|
| 89 |
+
data_table_html = wrap_html(f"<h2>OHLC Table</h2>{df.to_html(index=False, escape=False)}")
|
| 90 |
+
|
| 91 |
+
# Combine full dashboard
|
| 92 |
+
full_html = chart_html + table_html + data_table_html
|
| 93 |
+
|
| 94 |
+
# Cache HTML
|
| 95 |
+
persist.save(key, full_html, "html")
|
| 96 |
+
return full_html
|
| 97 |
+
|
| 98 |
+
except Exception as e:
|
| 99 |
+
return wrap_html(f"<h1>Error fetch_daily: {e}</h1><pre>{traceback.format_exc()}</pre>")
|