eshan6704 commited on
Commit
d51275f
·
verified ·
1 Parent(s): 0fbfd02

Update daily.py

Browse files
Files changed (1) hide show
  1. daily.py +22 -382
daily.py CHANGED
@@ -1,395 +1,35 @@
1
  # daily.py
2
  import yfinance as yf
3
  import pandas as pd
4
- import numpy as np
5
- import plotly.graph_objs as go
6
- from plotly.subplots import make_subplots
7
 
8
- # Try to import TA-Lib; if unavailable, will use fallback implementations
9
- try:
10
- import talib
11
- TALIB_AVAILABLE = True
12
- except Exception:
13
- TALIB_AVAILABLE = False
14
 
15
- from common import wrap_html, STYLE_BLOCK
 
 
 
 
16
 
17
- # --- Helper fallback implementations (used if TA-Lib isn't available) ---
18
- def sma_fallback(series, period):
19
- return series.rolling(period).mean()
20
 
21
- def ema_fallback(series, period):
22
- return series.ewm(span=period, adjust=False).mean()
23
-
24
- def macd_fallback(close, fast=12, slow=26, signal=9):
25
- fast_ema = ema_fallback(close, fast)
26
- slow_ema = ema_fallback(close, slow)
27
- macd = fast_ema - slow_ema
28
- macd_signal = ema_fallback(macd, signal)
29
- macd_hist = macd - macd_signal
30
- return macd, macd_signal, macd_hist
31
-
32
- def atr_fallback(high, low, close, period=14):
33
- tr1 = high - low
34
- tr2 = (high - close.shift()).abs()
35
- tr3 = (low - close.shift()).abs()
36
- tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
37
- atr = tr.rolling(period).mean()
38
- return atr
39
-
40
- def stoch_fallback(high, low, close, k_period=14, d_period=3):
41
- low_min = low.rolling(k_period).min()
42
- high_max = high.rolling(k_period).max()
43
- k = 100 * (close - low_min) / (high_max - low_min)
44
- d = k.rolling(d_period).mean()
45
- return k, d
46
-
47
- # SuperTrend (custom)
48
- def supertrend_fallback(df, period=10, multiplier=3):
49
- hl2 = (df['High'] + df['Low']) / 2
50
- atr = atr_fallback(df['High'], df['Low'], df['Close'], period=period)
51
- upperband = hl2 + multiplier * atr
52
- lowerband = hl2 - multiplier * atr
53
-
54
- st = pd.Series(index=df.index, dtype=float)
55
- trend = pd.Series(index=df.index, dtype=bool) # True = uptrend, False = downtrend
56
-
57
- # initialize
58
- st.iloc[0] = np.nan
59
- trend.iloc[0] = True
60
-
61
- for i in range(1, len(df)):
62
- if df['Close'].iat[i] > upperband.iat[i-1]:
63
- trend.iat[i] = True
64
- elif df['Close'].iat[i] < lowerband.iat[i-1]:
65
- trend.iat[i] = False
66
- else:
67
- trend.iat[i] = trend.iat[i-1]
68
- if trend.iat[i] and lowerband.iat[i] < st.iat[i-1]:
69
- lowerband.iat[i] = st.iat[i-1]
70
- if not trend.iat[i] and upperband.iat[i] > st.iat[i-1]:
71
- upperband.iat[i] = st.iat[i-1]
72
-
73
- st.iat[i] = lowerband.iat[i] if trend.iat[i] else upperband.iat[i]
74
-
75
- return st, trend
76
-
77
- # ZigZag (simple percent threshold)
78
- def zigzag_fallback(close_series, pct=5.0):
79
- zz = pd.Series(index=close_series.index, dtype=float)
80
- last_extreme_idx = 0
81
- last_extreme_price = close_series.iat[0]
82
- last_trend = 0 # 1 = up, -1 = down, 0 = unknown
83
- for i in range(1, len(close_series)):
84
- change = (close_series.iat[i] - last_extreme_price) / last_extreme_price * 100.0
85
- if last_trend >= 0 and change <= -pct:
86
- zz.iat[i] = close_series.iat[i]
87
- last_extreme_idx = i
88
- last_extreme_price = close_series.iat[i]
89
- last_trend = -1
90
- elif last_trend <= 0 and change >= pct:
91
- zz.iat[i] = close_series.iat[i]
92
- last_extreme_idx = i
93
- last_extreme_price = close_series.iat[i]
94
- last_trend = 1
95
- return zz
96
-
97
- # Swing High/Low via rolling window
98
- def swing_high_low(df, window=5):
99
- df = df.copy()
100
- df['SwingHigh'] = df['High'].rolling(window, center=True).max()
101
- df['SwingLow'] = df['Low'].rolling(window, center=True).min()
102
- # Only keep values where the center equals the extreme (to mark pivot)
103
- center = window // 2
104
- swing_high = [np.nan]*len(df)
105
- swing_low = [np.nan]*len(df)
106
- highs = df['High'].values
107
- lows = df['Low'].values
108
- for i in range(center, len(df)-center):
109
- window_high = highs[i-center:i+center+1]
110
- window_low = lows[i-center:i+center+1]
111
- if highs[i] == np.max(window_high):
112
- swing_high[i] = highs[i]
113
- if lows[i] == np.min(window_low):
114
- swing_low[i] = lows[i]
115
- df['SwingHigh'] = swing_high
116
- df['SwingLow'] = swing_low
117
- return df['SwingHigh'], df['SwingLow']
118
-
119
- # Keltner Channel (EMA ± ATR * mult)
120
- def keltner_channel(df, ema_period=20, atr_period=10, atr_mult=2):
121
- if TALIB_AVAILABLE:
122
- ema = talib.EMA(df['Close'], timeperiod=ema_period)
123
- atr = talib.ATR(df['High'], df['Low'], df['Close'], timeperiod=atr_period)
124
- else:
125
- ema = ema_fallback(df['Close'], ema_period)
126
- atr = atr_fallback(df['High'], df['Low'], df['Close'], period=atr_period)
127
- upper = ema + atr_mult * atr
128
- lower = ema - atr_mult * atr
129
- return upper, lower
130
-
131
- # --- Main fetch function ---
132
- def fetch_daily(symbol,
133
- period="1y",
134
- interval="1d",
135
- supertrend_period=10,
136
- supertrend_mult=3,
137
- keltner_ema=20,
138
- keltner_atr=10,
139
- keltner_mult=2,
140
- zigzag_pct=5.0):
141
- """
142
- Returns full HTML with multi-panel Plotly chart (price, volume, MACD, Stochastic)
143
- and controls (buttons) to toggle indicators.
144
- """
145
- yfsymbol = f"{symbol}.NS"
146
- try:
147
- df = yf.download(yfsymbol, period=period, interval=interval).round(6)
148
- except Exception as e:
149
- return wrap_html("Error", f"<h1>Error fetching data for {symbol}</h1><p>{str(e)}</p>")
150
-
151
- if df is None or df.empty:
152
- return wrap_html("No Data", f"<h1>No {interval} data for {symbol} (period={period})</h1>")
153
-
154
- # Ensure datetime index is proper
155
- df = df.copy()
156
- if isinstance(df.columns, pd.MultiIndex):
157
- df.columns = df.columns.get_level_values(0)
158
-
159
- # Compute indicators (TA-Lib if available, else fallback)
160
- if TALIB_AVAILABLE:
161
- # moving averages
162
- df['SMA20'] = talib.SMA(df['Close'], timeperiod=20)
163
- df['SMA50'] = talib.SMA(df['Close'], timeperiod=50)
164
- df['EMA20'] = talib.EMA(df['Close'], timeperiod=20)
165
- # MACD
166
- macd, macd_signal, macd_hist = talib.MACD(df['Close'], fastperiod=12, slowperiod=26, signalperiod=9)
167
- df['MACD'], df['MACD_Signal'], df['MACD_Hist'] = macd, macd_signal, macd_hist
168
- # Stochastic
169
- slowk, slowd = talib.STOCH(df['High'], df['Low'], df['Close'], fastk_period=14, slowk_period=3, slowk_matype=0, slowd_period=3, slowd_matype=0)
170
- df['StochK'], df['StochD'] = slowk, slowd
171
- # ATR for SuperTrend/Keltner
172
- df['ATR14'] = talib.ATR(df['High'], df['Low'], df['Close'], timeperiod=14)
173
- else:
174
- # SMA/EMA fallback
175
- df['SMA20'] = sma_fallback(df['Close'], 20)
176
- df['SMA50'] = sma_fallback(df['Close'], 50)
177
- df['EMA20'] = ema_fallback(df['Close'], 20)
178
- # MACD fallback
179
- macd, macd_signal, macd_hist = macd_fallback(df['Close'], 12, 26, 9)
180
- df['MACD'], df['MACD_Signal'], df['MACD_Hist'] = macd, macd_signal, macd_hist
181
- # Stoch fallback
182
- stoch_k, stoch_d = stoch_fallback(df['High'], df['Low'], df['Close'], 14, 3)
183
- df['StochK'], df['StochD'] = stoch_k, stoch_d
184
- df['ATR14'] = atr_fallback(df['High'], df['Low'], df['Close'], period=14)
185
-
186
- # SuperTrend (custom using ATR)
187
- try:
188
- st_values, st_trend = supertrend_fallback(df, period=supertrend_period, multiplier=supertrend_mult)
189
- df['SuperTrend'] = st_values
190
- df['SuperTrendDir'] = st_trend
191
- except Exception:
192
- # if fails, fill NaN
193
- df['SuperTrend'] = np.nan
194
- df['SuperTrendDir'] = np.nan
195
-
196
- # Keltner Channel
197
- try:
198
- kc_upper, kc_lower = keltner_channel(df, ema_period=keltner_ema, atr_period=keltner_atr, atr_mult=keltner_mult)
199
- df['KC_Upper'] = kc_upper
200
- df['KC_Lower'] = kc_lower
201
- except Exception:
202
- df['KC_Upper'] = np.nan
203
- df['KC_Lower'] = np.nan
204
-
205
- # ZigZag
206
- try:
207
- df['ZigZag'] = zigzag_fallback(df['Close'], pct=zigzag_pct)
208
- except Exception:
209
- df['ZigZag'] = np.nan
210
-
211
- # Swing High/Low
212
- try:
213
- sh, sl = swing_high_low(df, window=5)
214
- df['SwingHigh'] = sh
215
- df['SwingLow'] = sl
216
- except Exception:
217
- df['SwingHigh'] = np.nan
218
- df['SwingLow'] = np.nan
219
-
220
- # OBV or Volume smoothing if needed (not required but left as data)
221
- df['VolumeSmoothed'] = df['Volume'].rolling(3).mean()
222
-
223
- # Build Plotly subplots: 4 rows (price, volume, MACD, Stochastic)
224
- fig = make_subplots(rows=4, cols=1, shared_xaxes=True,
225
- row_heights=[0.5, 0.12, 0.18, 0.18],
226
- specs=[[{"secondary_y": False}],
227
- [{"secondary_y": False}],
228
- [{"secondary_y": False}],
229
- [{"secondary_y": False}]])
230
-
231
- x = df.index
232
-
233
- # Row 1: Candlestick
234
- fig.add_trace(go.Candlestick(x=x, open=df['Open'], high=df['High'], low=df['Low'], close=df['Close'],
235
- name='Price', increasing_line_color='green', decreasing_line_color='red'), row=1, col=1)
236
-
237
- # SuperTrend (line, colored by trend)
238
- fig.add_trace(go.Scatter(x=x, y=df['SuperTrend'], mode='lines', name='SuperTrend', visible=True,
239
- line=dict(width=2, dash='dash')), row=1, col=1)
240
-
241
- # SMA & EMA
242
- fig.add_trace(go.Scatter(x=x, y=df['SMA20'], mode='lines', name='SMA20', visible=True, line=dict(width=1)), row=1, col=1)
243
- fig.add_trace(go.Scatter(x=x, y=df['SMA50'], mode='lines', name='SMA50', visible=False, line=dict(width=1)), row=1, col=1)
244
- fig.add_trace(go.Scatter(x=x, y=df['EMA20'], mode='lines', name='EMA20', visible=False, line=dict(width=1, dash='dot')), row=1, col=1)
245
-
246
- # Keltner Channels
247
- fig.add_trace(go.Scatter(x=x, y=df['KC_Upper'], mode='lines', name='KC Upper', visible=False, line=dict(width=1)), row=1, col=1)
248
- fig.add_trace(go.Scatter(x=x, y=df['KC_Lower'], mode='lines', name='KC Lower', visible=False, line=dict(width=1)), row=1, col=1)
249
-
250
- # ZigZag and Swing markers as markers on price panel
251
- fig.add_trace(go.Scatter(x=x, y=df['ZigZag'], mode='markers', name='ZigZag', visible=False,
252
- marker=dict(symbol='diamond', size=8)), row=1, col=1)
253
- fig.add_trace(go.Scatter(x=x, y=df['SwingHigh'], mode='markers', name='Swing High', visible=False,
254
- marker=dict(symbol='triangle-up', size=8, color='purple')), row=1, col=1)
255
- fig.add_trace(go.Scatter(x=x, y=df['SwingLow'], mode='markers', name='Swing Low', visible=False,
256
- marker=dict(symbol='triangle-down', size=8, color='brown')), row=1, col=1)
257
-
258
- # Row 2: Volume
259
- fig.add_trace(go.Bar(x=x, y=df['Volume'], name='Volume', marker_color='lightblue', visible=True), row=2, col=1)
260
-
261
- # Row 3: MACD (line + signal + histogram as bar)
262
- fig.add_trace(go.Scatter(x=x, y=df['MACD'], mode='lines', name='MACD', visible=False), row=3, col=1)
263
- fig.add_trace(go.Scatter(x=x, y=df['MACD_Signal'], mode='lines', name='MACD Signal', visible=False), row=3, col=1)
264
- fig.add_trace(go.Bar(x=x, y=df['MACD_Hist'], name='MACD Hist', visible=False), row=3, col=1)
265
-
266
- # Row 4: Stochastic (%K & %D)
267
- fig.add_trace(go.Scatter(x=x, y=df['StochK'], mode='lines', name='Stoch %K', visible=False), row=4, col=1)
268
- fig.add_trace(go.Scatter(x=x, y=df['StochD'], mode='lines', name='Stoch %D', visible=False), row=4, col=1)
269
-
270
- # Layout defaults
271
- fig.update_layout(
272
- template="plotly_white",
273
- xaxis_rangeslider_visible=False,
274
- height=1000,
275
- legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='right', x=1)
276
  )
277
 
278
- # Prepare updatemenu buttons for toggling indicators
279
- # Determine trace indices in the order they were added:
280
- # 0 price candlestick
281
- # 1 supertrend
282
- # 2 SMA20
283
- # 3 SMA50
284
- # 4 EMA20
285
- # 5 KC Upper
286
- # 6 KC Lower
287
- # 7 ZigZag
288
- # 8 SwingHigh
289
- # 9 SwingLow
290
- # 10 Volume
291
- # 11 MACD
292
- # 12 MACD Signal
293
- # 13 MACD Hist
294
- # 14 StochK
295
- # 15 StochD
296
-
297
- total_traces = len(fig.data) # should be 16
298
-
299
- # Helper to create visible array with selective traces visible
300
- def visible_array(show_indices):
301
- arr = [False] * total_traces
302
- # keep price and volume visible by default in any selection for context
303
- arr[0] = True # candle
304
- arr[10] = True # volume (index may vary—check length; we've added bar at position 10)
305
- for i in show_indices:
306
- if 0 <= i < total_traces:
307
- arr[i] = True
308
- return arr
309
-
310
- # Map indicator buttons to the trace indices they affect
311
- buttons = []
312
-
313
- # SuperTrend
314
- buttons.append(dict(label="SuperTrend",
315
- method="restyle",
316
- args=[{"visible": visible_array([1])}]))
317
-
318
- # SMA20
319
- buttons.append(dict(label="SMA20",
320
- method="restyle",
321
- args=[{"visible": visible_array([2])}]))
322
-
323
- # SMA50
324
- buttons.append(dict(label="SMA50",
325
- method="restyle",
326
- args=[{"visible": visible_array([3])}]))
327
-
328
- # EMA20
329
- buttons.append(dict(label="EMA20",
330
- method="restyle",
331
- args=[{"visible": visible_array([4])}]))
332
-
333
- # Keltner Channel
334
- buttons.append(dict(label="Keltner",
335
- method="restyle",
336
- args=[{"visible": visible_array([5,6])}]))
337
-
338
- # ZigZag
339
- buttons.append(dict(label="ZigZag",
340
- method="restyle",
341
- args=[{"visible": visible_array([7])}]))
342
-
343
- # Swing HI/LO
344
- buttons.append(dict(label="Swing H/L",
345
- method="restyle",
346
- args=[{"visible": visible_array([8,9])}]))
347
-
348
- # MACD
349
- buttons.append(dict(label="MACD",
350
- method="restyle",
351
- args=[{"visible": visible_array([11,12,13])}]))
352
-
353
- # Stochastic
354
- buttons.append(dict(label="Stochastic",
355
- method="restyle",
356
- args=[{"visible": visible_array([14,15])}]))
357
-
358
- # Show All (turn on all traces)
359
- buttons.append(dict(label="All On",
360
- method="restyle",
361
- args=[{"visible": [True]*total_traces}]))
362
-
363
- # All Off (only show price and volume)
364
- buttons.append(dict(label="All Off",
365
- method="restyle",
366
- args=[{"visible": visible_array([])}]))
367
-
368
- # Reset (default view: price, volume, supertrend, SMA20)
369
- buttons.append(dict(label="Reset",
370
- method="restyle",
371
- args=[{"visible": visible_array([1,2])}]))
372
-
373
- fig.update_layout(
374
- updatemenus=[dict(type="buttons", direction="down", buttons=buttons, x=1.02, xanchor="left", y=0.95, yanchor="top")]
375
- )
376
-
377
- # Finalize: add a table (recent 30 rows) below chart area as HTML
378
- table_html = df.tail(30)[['Open', 'High', 'Low', 'Close', 'Volume']].round(2).to_html(classes="styled-table", border=0)
379
-
380
- chart_div = fig.to_html(full_html=False, include_plotlyjs='cdn')
381
-
382
- content_html = f"""
383
- <div class='card'>
384
- <h2>Daily Chart - {symbol.upper()}</h2>
385
- <div class='card-content-grid'>
386
- <div style="width:100%">{chart_div}</div>
387
- </div>
388
- </div>
389
- <div class='big-box'>
390
- <h2>Recent Daily Data (last 30 rows)</h2>
391
  {table_html}
392
  </div>
393
  """
394
 
395
- return wrap_html(f"Daily Chart for {symbol}", content_html, style_block=STYLE_BLOCK)
 
 
 
 
1
  # daily.py
2
  import yfinance as yf
3
  import pandas as pd
4
+ from chart_builder import build_chart
 
 
5
 
6
+ def fetch_daily(symbol):
7
+ yfs = f"{symbol}.NS"
8
+ df = yf.download(yfs, period="1y", interval="1d").round(2)
 
 
 
9
 
10
+ if df.empty:
11
+ return {
12
+ "html": f"<h1>No daily data for {symbol}</h1>",
13
+ "data": {}
14
+ }
15
 
16
+ chart_html = build_chart(df)
 
 
17
 
18
+ table_html = df.tail(30).to_html(
19
+ classes="styled-table",
20
+ border=0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  )
22
 
23
+ final = f"""
24
+ <div class="group">
25
+ <h2>Daily Chart {symbol}</h2>
26
+ {chart_html}
27
+ <h3>Last 30 Days Data</h3>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  {table_html}
29
  </div>
30
  """
31
 
32
+ return {
33
+ "html": final,
34
+ "data": df.tail(30).to_dict()
35
+ }