eshan6704 commited on
Commit
97cab92
·
verified ·
1 Parent(s): ab78936

Update app/daily.py

Browse files
Files changed (1) hide show
  1. app/daily.py +93 -36
app/daily.py CHANGED
@@ -4,16 +4,51 @@ 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")
@@ -22,76 +57,98 @@ def fetch_daily(symbol, date_end, date_start, b2_save=False):
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
 
 
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, io, base64
8
 
9
  from . import persist
10
  from . import backblaze as b2
11
  from .common import wrap_html, format_large_number
12
 
13
+ # ===========================================================
14
+ # Candlestick Pattern Detection
15
+ # ===========================================================
16
+ def detect_patterns(df):
17
+ patterns = []
18
+
19
+ for i in range(1, len(df)):
20
+ open_today, close_today = df.loc[i, "Open"], df.loc[i, "Close"]
21
+ open_prev, close_prev = df.loc[i-1, "Open"], df.loc[i-1, "Close"]
22
+ high, low = df.loc[i, "High"], df.loc[i, "Low"]
23
+
24
+ # Bullish Engulfing
25
+ if close_prev < open_prev and close_today > open_today and close_today > open_prev and open_today < close_prev:
26
+ patterns.append({"Date": df.loc[i, "Date"], "Pattern": "Bullish Engulfing"})
27
+ # Bearish Engulfing
28
+ elif close_prev > open_prev and close_today < open_today and open_today > close_prev and close_today < open_prev:
29
+ patterns.append({"Date": df.loc[i, "Date"], "Pattern": "Bearish Engulfing"})
30
+ # Doji
31
+ elif abs(close_today - open_today)/ (high-low+1e-6) < 0.1:
32
+ patterns.append({"Date": df.loc[i, "Date"], "Pattern": "Doji"})
33
+ # Hammer / Hanging Man
34
+ elif (high - max(open_today, close_today)) > 2*(max(open_today, close_today)-min(open_today, close_today)) and \
35
+ (min(open_today, close_today) - low) < 0.1*(high-low):
36
+ if close_today > open_today:
37
+ patterns.append({"Date": df.loc[i, "Date"], "Pattern": "Hammer"})
38
+ else:
39
+ patterns.append({"Date": df.loc[i, "Date"], "Pattern": "Hanging Man"})
40
+ # Gap Up / Gap Down
41
+ if df.loc[i, "Open"] > df.loc[i-1, "Close"] * 1.01:
42
+ patterns.append({"Date": df.loc[i, "Date"], "Pattern": "Gap Up"})
43
+ elif df.loc[i, "Open"] < df.loc[i-1, "Close"] * 0.99:
44
+ patterns.append({"Date": df.loc[i, "Date"], "Pattern": "Gap Down"})
45
+
46
+ return pd.DataFrame(patterns)
47
+
48
+ # ===========================================================
49
+ # Ultimate Daily Analysis Dashboard
50
+ # ===========================================================
51
  def fetch_daily(symbol, date_end, date_start, b2_save=False):
 
 
 
52
  key = f"daily_{symbol}"
53
  if persist.exists(key, "html"):
54
  cached = persist.load(key, "html")
 
57
  return cached
58
 
59
  try:
60
+ # Download data
61
  start = dt.strptime(date_start, "%d-%m-%Y").strftime("%Y-%m-%d")
62
  end = dt.strptime(date_end, "%d-%m-%Y").strftime("%Y-%m-%d")
 
63
  print(f"[{dt.now().strftime('%Y-%m-%d %H:%M:%S')}] Fetching daily for {symbol}")
64
  df = yf.download(symbol + ".NS", start=start, end=end)
65
  if df.empty:
66
  return wrap_html(f"<h1>No daily data for {symbol}</h1>")
67
 
 
68
  df.reset_index(inplace=True)
69
  df["Date"] = df["Date"].dt.strftime("%d-%b-%Y")
70
 
 
71
  if b2_save:
72
  b2.upload_file("eshanhf", f"daily/{symbol}.csv", df)
73
 
74
+ # Indicators
75
+ df["Daily Return %"] = df["Close"].pct_change()*100
76
  df["SMA20"] = df["Close"].rolling(20).mean()
77
  df["SMA50"] = df["Close"].rolling(50).mean()
78
  df["EMA20"] = df["Close"].ewm(span=20, adjust=False).mean()
79
+ df["EMA50"] = df["Close"].ewm(span=50, adjust=False).mean()
80
  df["UpperBB"] = df["Close"].rolling(20).mean() + 2*df["Close"].rolling(20).std()
81
  df["LowerBB"] = df["Close"].rolling(20).mean() - 2*df["Close"].rolling(20).std()
82
+ df["ATR"] = df["High"].combine(df["Low"], lambda h,l: h-l).rolling(14).mean()
83
 
84
  # Summary stats
85
  summary = pd.DataFrame({
86
+ "Metric": ["Start Date","End Date","Min Price","Max Price","Mean Price","Total Volume","Avg Daily Return %","Volatility ATR"],
87
+ "Value": [
88
+ df["Date"].iloc[0],
89
+ df["Date"].iloc[-1],
90
+ format_large_number(df["Close"].min()),
91
+ format_large_number(df["Close"].max()),
92
+ format_large_number(df["Close"].mean()),
93
+ format_large_number(df["Volume"].sum()),
94
+ f"{df['Daily Return %'].mean():.2f}%",
95
+ f"{df['ATR'].mean():.2f}"
96
+ ]
97
  })
98
 
99
+ # Detect patterns
100
+ patterns_df = detect_patterns(df)
 
 
 
101
 
102
+ # Plotly dashboard
103
+ fig = make_subplots(rows=4, cols=1, shared_xaxes=True,
104
+ vertical_spacing=0.05,
105
+ row_heights=[0.4,0.2,0.2,0.2],
106
+ specs=[[{}],[{}],[{}],[{}]])
107
 
108
+ # Candlestick + SMA/EMA/Bollinger
109
+ fig.add_trace(go.Candlestick(x=df["Date"], open=df["Open"], high=df["High"], low=df["Low"], close=df["Close"], name="OHLC"), row=1, col=1)
110
  fig.add_trace(go.Scatter(x=df["Date"], y=df["SMA20"], mode="lines", name="SMA20"), row=1, col=1)
111
  fig.add_trace(go.Scatter(x=df["Date"], y=df["SMA50"], mode="lines", name="SMA50"), row=1, col=1)
112
  fig.add_trace(go.Scatter(x=df["Date"], y=df["EMA20"], mode="lines", name="EMA20"), row=1, col=1)
113
+ fig.add_trace(go.Scatter(x=df["Date"], y=df["EMA50"], mode="lines", name="EMA50"), row=1, col=1)
 
114
  fig.add_trace(go.Scatter(x=df["Date"], y=df["UpperBB"], mode="lines", name="UpperBB", line=dict(dash="dot")), row=1, col=1)
115
  fig.add_trace(go.Scatter(x=df["Date"], y=df["LowerBB"], mode="lines", name="LowerBB", line=dict(dash="dot")), row=1, col=1)
116
 
117
+ # Highlight patterns on chart
118
+ for _, row in patterns_df.iterrows():
119
+ fig.add_trace(go.Scatter(
120
+ x=[row["Date"]], y=[df.loc[df["Date"]==row["Date"], "High"].values[0]*1.01],
121
+ mode="markers+text",
122
+ marker=dict(color="red", size=10, symbol="triangle-up"),
123
+ text=[row["Pattern"]],
124
+ textposition="top center",
125
+ showlegend=False
126
+ ), row=1, col=1)
127
+
128
+ # Volume
129
  fig.add_trace(go.Bar(x=df["Date"], y=df["Volume"], name="Volume"), row=2, col=1)
130
+ # Daily Return %
131
+ fig.add_trace(go.Scatter(x=df["Date"], y=df["Daily Return %"], mode="lines+markers", name="Daily Return %"), row=3, col=1)
132
+ # ATR
133
+ fig.add_trace(go.Scatter(x=df["Date"], y=df["ATR"], mode="lines", name="ATR"), row=4, col=1)
134
 
135
+ fig.update_layout(height=1000, width=1200, title=f"{symbol} Daily Analysis Dashboard", xaxis_rangeslider_visible=False)
 
 
136
  chart_html = fig.to_html(full_html=False, include_plotlyjs='cdn')
137
 
138
+ # Tables
139
+ table_html = wrap_html(f"<h2>Summary Stats</h2>{summary.to_html(index=False, escape=False)}")
140
  data_table_html = wrap_html(f"<h2>OHLC Table</h2>{df.to_html(index=False, escape=False)}")
141
+ patterns_html = wrap_html(f"<h2>Detected Patterns</h2>{patterns_df.to_html(index=False, escape=False)}")
142
+
143
+ # CSV download
144
+ csv_buffer = io.StringIO()
145
+ df.to_csv(csv_buffer, index=False)
146
+ csv_base64 = base64.b64encode(csv_buffer.getvalue().encode()).decode()
147
+ download_html = f'<a href="data:text/csv;base64,{csv_base64}" download="{symbol}_daily.csv">Download CSV</a>'
148
 
149
+ full_html = chart_html + table_html + patterns_html + data_table_html + download_html
 
150
 
151
+ # Cache
152
  persist.save(key, full_html, "html")
153
  return full_html
154