eshan6704 commited on
Commit
9fef9dc
·
verified ·
1 Parent(s): f8d060f

Update app/daily.py

Browse files
Files changed (1) hide show
  1. app/daily.py +175 -103
app/daily.py CHANGED
@@ -4,30 +4,31 @@ import pandas as pd
4
  from datetime import datetime as dt
5
  import traceback
6
  from . import persist
7
- from .common import wrap_html
8
- import plotly.graph_objects as go
9
- import plotly.express as px
10
- import plotly.io as pio
11
- from plotly.subplots import make_subplots
12
-
13
- # =========================
14
- # DAILY DATA FETCH
15
- # =========================
16
  def daily(symbol, date_end, date_start):
17
  start = dt.strptime(date_start, "%d-%m-%Y").strftime("%Y-%m-%d")
18
  end = dt.strptime(date_end, "%d-%m-%Y").strftime("%Y-%m-%d")
 
19
  df = yf.download(symbol + ".NS", start=start, end=end)
 
 
20
  if isinstance(df.columns, pd.MultiIndex):
21
  df.columns = df.columns.get_level_values(0)
 
22
  df.columns.name = None
23
  df.index.name = "Date"
24
  return df
25
 
26
- # =========================
 
27
  # DASHBOARD
28
- # =========================
29
  def fetch_daily(symbol, date_end, date_start):
30
  key = f"daily_{symbol}"
 
31
  if persist.exists(key, "html"):
32
  cached = persist.load(key, "html")
33
  if cached:
@@ -35,97 +36,168 @@ def fetch_daily(symbol, date_end, date_start):
35
 
36
  try:
37
  df = daily(symbol, date_end, date_start)
38
- if df.empty:
39
- return wrap_html(f"<h1>No daily data for {symbol}</h1>")
40
-
41
- # Ensure numeric columns
42
- for col in ["Open","High","Low","Close","Volume"]:
43
- if col in df.columns:
44
- df[col] = pd.to_numeric(df[col], errors='coerce')
45
-
46
- df = df.dropna(subset=["Open","High","Low","Close","Volume"]).reset_index()
47
- df["Date"] = pd.to_datetime(df["Date"], errors='coerce')
48
- df = df.dropna(subset=["Date"]).reset_index(drop=True)
49
- df["Date"] = df["Date"].dt.strftime("%d-%b-%Y")
50
- df["Change %"] = ((df["Close"] - df["Open"]) / df["Open"] * 100).round(2)
51
-
52
- # -------------------------
53
- # HTML Table
54
- # -------------------------
55
- html_table = '<div style="max-height:300px; overflow:auto; font-family:Arial,sans-serif; margin-bottom:20px;">'
56
- html_table += '<table border="1" style="border-collapse:collapse; width:100%;">'
57
- html_table += '''
58
- <thead style="
59
- position:sticky;
60
- top:0;
61
- background: linear-gradient(to right,#1a4f8a,#4a7ac7);
62
- color:white;
63
- font-weight:bold;
64
- text-align:center;">
65
- '''
66
- html_table += '<tr><th>Date</th><th>Open</th><th>High</th><th>Low</th>'
67
- html_table += '<th>Close</th><th>Volume</th><th>Change %</th></tr></thead><tbody>'
68
- for idx, r in df.iterrows():
69
- row_color = "#e8f5e9" if idx % 2 == 0 else "#f5f5f5"
70
- change_color = "green" if r["Change %"] > 0 else "red" if r["Change %"] < 0 else "black"
71
- html_table += f'<tr style="background:{row_color};">'
72
- html_table += f'<td>{r["Date"]}</td>'
73
- html_table += f'<td>{r["Open"]}</td>'
74
- html_table += f'<td>{r["High"]}</td>'
75
- html_table += f'<td>{r["Low"]}</td>'
76
- html_table += f'<td>{r["Close"]}</td>'
77
- html_table += f'<td>{r["Volume"]}</td>'
78
- html_table += f'<td style="color:{change_color}; font-weight:600;">{r["Change %"]}%</td>'
79
- html_table += '</tr>'
80
- html_table += '</tbody></table></div>'
81
-
82
- # -------------------------
83
- # COMBINED PLOTLY FIGURE
84
- # -------------------------
85
- fig = make_subplots(
86
- rows=3, cols=1,
87
- shared_xaxes=True,
88
- row_heights=[0.5, 0.25, 0.25],
89
- vertical_spacing=0.05,
90
- subplot_titles=("Candlestick & Volume", "MA20/MA50", "Daily % Change")
91
- )
92
-
93
- # Candlestick
94
- fig.add_trace(go.Candlestick(
95
- x=df['Date'], open=df['Open'], high=df['High'],
96
- low=df['Low'], close=df['Close'], name='Price',
97
- increasing_line_color='green', decreasing_line_color='red'
98
- ), row=1, col=1)
99
-
100
- # Volume as bar
101
- fig.add_trace(go.Bar(
102
- x=df['Date'], y=df['Volume'], name='Volume',
103
- marker_color='blue', opacity=0.3
104
- ), row=1, col=1)
105
-
106
- # MA20 & MA50
107
- df['MA20'] = df['Close'].rolling(20).mean()
108
- df['MA50'] = df['Close'].rolling(50).mean()
109
- fig.add_trace(go.Scatter(x=df['Date'], y=df['Close'], mode='lines', name='Close'), row=2, col=1)
110
- fig.add_trace(go.Scatter(x=df['Date'], y=df['MA20'], mode='lines', name='MA20'), row=2, col=1)
111
- fig.add_trace(go.Scatter(x=df['Date'], y=df['MA50'], mode='lines', name='MA50'), row=2, col=1)
112
-
113
- # Daily % change
114
- fig.add_trace(go.Bar(
115
- x=df['Date'], y=df['Change %'], name='Change %',
116
- marker_color=df['Change %'].apply(lambda x: 'green' if x>0 else 'red')
117
- ), row=3, col=1)
118
-
119
- fig.update_layout(height=900, showlegend=True, xaxis_rangeslider_visible=False)
120
-
121
- plot_html = pio.to_html(fig, full_html=False, include_plotlyjs='cdn')
122
-
123
- # -------------------------
124
- # COMBINE TABLE + CHART
125
- # -------------------------
126
- full_html = f'<div id="daily_dashboard">{html_table}{plot_html}</div>'
127
- persist.save(key, full_html, "html")
128
- return full_html
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
 
130
  except Exception as e:
131
- return wrap_html(f"<h1>Error fetch_daily: {e}</h1><pre>{traceback.format_exc()}</pre>")
 
4
  from datetime import datetime as dt
5
  import traceback
6
  from . import persist
7
+
8
+ # ============================================================
9
+ # DAILY DATA FETCH (DO NOT CHANGE)
10
+ # ============================================================
 
 
 
 
 
11
  def daily(symbol, date_end, date_start):
12
  start = dt.strptime(date_start, "%d-%m-%Y").strftime("%Y-%m-%d")
13
  end = dt.strptime(date_end, "%d-%m-%Y").strftime("%Y-%m-%d")
14
+
15
  df = yf.download(symbol + ".NS", start=start, end=end)
16
+
17
+ # Flatten multi-index columns
18
  if isinstance(df.columns, pd.MultiIndex):
19
  df.columns = df.columns.get_level_values(0)
20
+
21
  df.columns.name = None
22
  df.index.name = "Date"
23
  return df
24
 
25
+
26
+ # ============================================================
27
  # DASHBOARD
28
+ # ============================================================
29
  def fetch_daily(symbol, date_end, date_start):
30
  key = f"daily_{symbol}"
31
+
32
  if persist.exists(key, "html"):
33
  cached = persist.load(key, "html")
34
  if cached:
 
36
 
37
  try:
38
  df = daily(symbol, date_end, date_start)
39
+ if df is None or df.empty:
40
+ return "<h1>No data</h1>"
41
+
42
+ # -------------------------------
43
+ # CLEAN & FORMAT
44
+ # -------------------------------
45
+ df = df.reset_index()
46
+
47
+ df["Date"] = pd.to_datetime(df["Date"], errors="coerce")
48
+ df = df.dropna(subset=["Date"])
49
+
50
+ for c in ["Open", "High", "Low", "Close", "Volume"]:
51
+ df[c] = pd.to_numeric(df[c], errors="coerce")
52
+
53
+ df = df.dropna()
54
+ df["DateStr"] = df["Date"].dt.strftime("%d-%b-%Y")
55
+
56
+ df["MA20"] = df["Close"].rolling(20).mean()
57
+ df["MA50"] = df["Close"].rolling(50).mean()
58
+
59
+ # -------------------------------
60
+ # TABLE HTML
61
+ # -------------------------------
62
+ table_rows = ""
63
+ for i, r in df.iterrows():
64
+ color = "#e8f5e9" if i % 2 == 0 else "#f5f5f5"
65
+ table_rows += f"""
66
+ <tr style="background:{color}">
67
+ <td>{r['DateStr']}</td>
68
+ <td>{r['Open']:.2f}</td>
69
+ <td>{r['High']:.2f}</td>
70
+ <td>{r['Low']:.2f}</td>
71
+ <td>{r['Close']:.2f}</td>
72
+ <td>{int(r['Volume'])}</td>
73
+ </tr>
74
+ """
75
+
76
+ table_html = f"""
77
+ <div class="table-wrap">
78
+ <table>
79
+ <thead>
80
+ <tr>
81
+ <th>Date</th><th>Open</th><th>High</th>
82
+ <th>Low</th><th>Close</th><th>Volume</th>
83
+ </tr>
84
+ </thead>
85
+ <tbody>{table_rows}</tbody>
86
+ </table>
87
+ </div>
88
+ """
89
+
90
+ # -------------------------------
91
+ # FULL HTML (IMPORTANT)
92
+ # -------------------------------
93
+ html = f"""
94
+ <!DOCTYPE html>
95
+ <html>
96
+ <head>
97
+ <meta charset="utf-8">
98
+ <title>{symbol} Daily Dashboard</title>
99
+
100
+ <script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
101
+
102
+ <style>
103
+ body {{
104
+ font-family: Arial, sans-serif;
105
+ margin: 10px;
106
+ background: #f4f6f9;
107
+ }}
108
+
109
+ .table-wrap {{
110
+ max-height: 300px;
111
+ overflow-y: auto;
112
+ margin-bottom: 20px;
113
+ }}
114
+
115
+ table {{
116
+ border-collapse: collapse;
117
+ width: 100%;
118
+ background: white;
119
+ }}
120
+
121
+ th {{
122
+ position: sticky;
123
+ top: 0;
124
+ background: linear-gradient(to right,#1a4f8a,#4a7ac7);
125
+ color: white;
126
+ padding: 6px;
127
+ }}
128
+
129
+ td {{
130
+ padding: 6px;
131
+ text-align: right;
132
+ }}
133
+
134
+ td:first-child {{
135
+ text-align: left;
136
+ }}
137
+ .chart {{
138
+ margin-bottom: 30px;
139
+ }}
140
+ </style>
141
+ </head>
142
+
143
+ <body>
144
+
145
+ <h2>{symbol} – Daily Price Table</h2>
146
+ {table_html}
147
+
148
+ <h2>Candlestick & Volume</h2>
149
+ <div id="candle" class="chart"></div>
150
+
151
+ <h2>Moving Averages</h2>
152
+ <div id="ma" class="chart"></div>
153
+
154
+ <script>
155
+ const dates = {df["DateStr"].tolist()};
156
+ const openp = {df["Open"].round(2).tolist()};
157
+ const highp = {df["High"].round(2).tolist()};
158
+ const lowp = {df["Low"].round(2).tolist()};
159
+ const closep= {df["Close"].round(2).tolist()};
160
+ const volume= {df["Volume"].astype(int).tolist()};
161
+ const ma20 = {df["MA20"].round(2).fillna(None).tolist()};
162
+ const ma50 = {df["MA50"].round(2).fillna(None).tolist()};
163
+
164
+ Plotly.newPlot("candle", [
165
+ {{
166
+ x: dates,
167
+ open: openp,
168
+ high: highp,
169
+ low: lowp,
170
+ close: closep,
171
+ type: "candlestick",
172
+ name: "Price"
173
+ }},
174
+ {{
175
+ x: dates,
176
+ y: volume,
177
+ type: "bar",
178
+ yaxis: "y2",
179
+ name: "Volume",
180
+ opacity: 0.3
181
+ }}
182
+ ], {{
183
+ yaxis: {{title: "Price"}},
184
+ yaxis2: {{overlaying: "y", side: "right", title: "Volume"}},
185
+ xaxis: {{rangeslider: {{visible:false}}}}
186
+ }});
187
+
188
+ Plotly.newPlot("ma", [
189
+ {{x: dates, y: closep, type:"scatter", name:"Close"}},
190
+ {{x: dates, y: ma20, type:"scatter", name:"MA20"}},
191
+ {{x: dates, y: ma50, type:"scatter", name:"MA50"}}
192
+ ]);
193
+ </script>
194
+
195
+ </body>
196
+ </html>
197
+ """
198
+
199
+ persist.save(key, html, "html")
200
+ return html
201
 
202
  except Exception as e:
203
+ return f"<pre>{traceback.format_exc()}</pre>"