Reality8081 commited on
Commit
22259b4
·
1 Parent(s): 515fc70

Update src

Browse files
Files changed (1) hide show
  1. app.py +33 -40
app.py CHANGED
@@ -45,7 +45,6 @@ def generate_quant_dashboard(ticker, model_name):
45
  preds, last_close, last_date, _ = predict_horizons(ticker, model_name)
46
 
47
  # 2. Lấy dữ liệu OHLCV 90 ngày để vẽ Candlestick & tính toán Context
48
- # Sử dụng yfinance trực tiếp để render UI mượt mà, độc lập với backend load_data nặng nề
49
  end_dt = datetime.strptime(last_date, '%Y-%m-%d') + timedelta(days=1)
50
  start_dt = end_dt - timedelta(days=90)
51
 
@@ -81,8 +80,7 @@ def generate_quant_dashboard(ticker, model_name):
81
  consensus_html = ""
82
  target_price = 0
83
 
84
- # --- TẠO BẢNG CONTEXT & CONSENSUS HIỂN THỊ 3 HORIZONS ---
85
- # Hàm tính giá trung bình giữa các model (đã có từ bước trước)
86
  def get_avg_price(h):
87
  if model_name == "Cả Hai":
88
  return (preds[h]["Linear Regression"]["pred_price"] + preds[h]["SVR"]["pred_price"]) / 2
@@ -96,38 +94,34 @@ def generate_quant_dashboard(ticker, model_name):
96
  else:
97
  return preds[h][model_name]["pred_return"]
98
 
99
- # Lấy giá mục tiêu
100
  target_1d = get_avg_price(1)
101
  target_7d = get_avg_price(7)
102
  target_21d = get_avg_price(21)
103
 
104
- # Lấy tỷ suất sinh lời (return)
105
  ret_1d = get_avg_return(1)
106
  ret_7d = get_avg_return(7)
107
  ret_21d = get_avg_return(21)
108
 
109
- # 1. LOGIC XÁC ĐỊNH TÍN HIỆU TỔNG THỂ (OVERALL SIGNAL)
110
- # Dựa vào mức tăng/giảm của dự báo T+21 (Dài nhất) và T+7 (Trung bình)
111
  if ret_21d > 0.02 and ret_7d > 0:
112
  signal_text = "STRONG BUY"
113
- signal_color = "#00ff00" # Xanh lá sáng
114
  bg_color = "rgba(0, 255, 0, 0.1)"
115
  elif ret_21d > 0:
116
  signal_text = "ACCUMULATE"
117
- signal_color = "#00cc66" # Xanh lục
118
  bg_color = "rgba(0, 204, 102, 0.1)"
119
  elif ret_21d < -0.02 and ret_7d < 0:
120
  signal_text = "STRONG SELL"
121
- signal_color = "#ff0000" # Đỏ
122
  bg_color = "rgba(255, 0, 0, 0.1)"
123
  else:
124
  signal_text = "REDUCE / SELL"
125
- signal_color = "#ff6666" # Đỏ nhạt
126
  bg_color = "rgba(255, 102, 102, 0.1)"
127
 
128
- # Hàm định dạng HTML cho % Return (Tự động đổi màu Xanh/Đỏ)
129
  def fmt_ret(val):
130
- color = "#00ff00" if val > 0 else "#ff4444"
131
  sign = "+" if val > 0 else ""
132
  return f"<span style='color: {color}; font-weight: bold;'>{sign}{val*100:.2f}%</span>"
133
 
@@ -166,12 +160,7 @@ def generate_quant_dashboard(ticker, model_name):
166
  </div>
167
  """
168
 
169
- # (Giữ nguyên đoạn tạo context_html ở phía dưới nếu bạn muốn hiển thị thêm thông tin khác)
170
-
171
- # 4. Market Context Panel (Technical Stats)
172
- rsi_color = "#ff3333" if rsi_val > 70 else ("#00ff00" if rsi_val < 30 else "#a8b2d1")
173
- macd_color = "#00ff00" if macd_h > 0 else "#ff3333"
174
-
175
  context_html = f"""
176
  <div style="background: var(--background-fill-secondary); border: 1px solid var(--border-color-primary); padding: 15px; border-radius: 5px; font-family: monospace;">
177
  <p style="color: var(--body-text-color-subdued); margin:0 0 10px 0; font-size:12px;">LAST CLOSE: {last_date}</p>
@@ -184,7 +173,7 @@ def generate_quant_dashboard(ticker, model_name):
184
  </div>
185
  """
186
 
187
- # 5. Vẽ biểu đồ Plotly cấp độ Institutional
188
  fig = make_subplots(rows=2, cols=1, shared_xaxes=True,
189
  vertical_spacing=0.03, row_heights=[0.75, 0.25],
190
  subplot_titles=(f"{ticker} - MULTI-HORIZON PROJECTIONS", "VOLUME"))
@@ -193,7 +182,7 @@ def generate_quant_dashboard(ticker, model_name):
193
  fig.add_trace(go.Candlestick(
194
  x=df_ui['Date'], open=df_ui['Open'], high=df_ui['High'],
195
  low=df_ui['Low'], close=df_ui['Close'], name='Price',
196
- increasing_line_color='#00ff00', decreasing_line_color='#ff3333'
197
  ), row=1, col=1)
198
 
199
  # SMAs
@@ -201,10 +190,12 @@ def generate_quant_dashboard(ticker, model_name):
201
  fig.add_trace(go.Scatter(x=df_ui['Date'], y=df_ui['SMA_50'], mode='lines', name='SMA 50', line=dict(color='#00bfff', width=1)), row=1, col=1)
202
 
203
  # Volume subplot
204
- colors = ['#00ff00' if row['Close'] >= row['Open'] else '#ff3333' for _, row in df_ui.iterrows()]
205
  fig.add_trace(go.Bar(x=df_ui['Date'], y=df_ui['Volume'], marker_color=colors, name='Volume'), row=2, col=1)
 
206
  x_future = [base_date, dates_future[1], dates_future[7], dates_future[21]]
207
- # --- Thêm điểm dự báo và Confidence Interval (Error Bands) dựa trên ATR ---
 
208
  if model_name in ["Linear Regression", "Cả Hai"]:
209
  y_lr = [last_close, preds[1]["Linear Regression"]["pred_price"],
210
  preds[7]["Linear Regression"]["pred_price"], preds[21]["Linear Regression"]["pred_price"]]
@@ -215,27 +206,32 @@ def generate_quant_dashboard(ticker, model_name):
215
  y_svr = [last_close, preds[1]["SVR"]["pred_price"],
216
  preds[7]["SVR"]["pred_price"], preds[21]["SVR"]["pred_price"]]
217
  fig.add_trace(go.Scatter(x=x_future, y=y_svr, mode='lines+markers', name='SVR Trajectory',
218
- line=dict(color='#00ffff', dash='dot'), marker=dict(size=8, symbol='diamond')), row=1, col=1)
 
219
  upper_band = [last_close, target_1d + atr_val*np.sqrt(1), target_7d + atr_val*np.sqrt(7), target_21d + atr_val*np.sqrt(21)]
220
  lower_band = [last_close, target_1d - atr_val*np.sqrt(1), target_7d - atr_val*np.sqrt(7), target_21d - atr_val*np.sqrt(21)]
221
- # Error Band (±1 ATR cho mức target)
222
- fig.add_trace(go.Scatter(x=x_future, y=upper_band, mode='lines', name='Risk Cone Upper', line=dict(color='rgba(0, 255, 0, 1)')), row=1, col=1)
223
- fig.add_trace(go.Scatter(x=x_future, y=lower_band, mode='lines', fill='tonexty', fillcolor='rgba(255, 255, 255, 0.5)', name='Risk Cone Lower', line=dict(color='rgba(255, 0, 0, 0.2)')), row=1, col=1)
 
224
 
225
- # Tối ưu giao diện Plotly Dark Mode
226
  fig.update_layout(
227
  margin=dict(l=40, r=40, t=40, b=40),
228
  xaxis_rangeslider_visible=False,
229
- legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
230
- font=dict(family="Courier New, monospace")
231
- # ĐÃ BỎ: plot_bgcolor và paper_bgcolor
 
232
  )
233
 
234
- fig.update_xaxes(showgrid=True, gridwidth=1)
235
- fig.update_yaxes(showgrid=True, gridwidth=1)
 
 
236
  return consensus_html, context_html, fig
237
 
238
- # --- KIẾN TRÚC GRADIO GIAO DIỆN DARK MODE ---
239
  css = """
240
  .gradio-container { max-width: 1400px !important; }
241
  #main-row { margin-top: 20px; }
@@ -253,7 +249,6 @@ function toggle_theme() {
253
  with gr.Blocks(title="Quant Terminal | Stock ML", css=css, theme=gr.themes.Monochrome()) as demo:
254
  with gr.Row():
255
  with gr.Column(scale=9):
256
- # Lưu ý: Đã bỏ style="color: #e6e6fa;" để chữ tự động đổi màu theo theme
257
  gr.Markdown("""
258
  <div style="padding: 10px 0; border-bottom: 2px solid var(--border-color-primary);">
259
  <h1 style="margin: 0; font-family: monospace;">⚡ QUANTRONIC ML TERMINAL v2.0</h1>
@@ -261,12 +256,10 @@ with gr.Blocks(title="Quant Terminal | Stock ML", css=css, theme=gr.themes.Monoc
261
  </div>
262
  """)
263
  with gr.Column(scale=1, min_width=150):
264
- # Thêm nút bấm chuyển đổi Theme
265
  btn_theme = gr.Button("🌓 Toggle Dark/Light", size="sm")
266
 
267
- # Đã sửa lỗi tại dòng này: Dùng elem_id thay vì style
268
  with gr.Row(elem_id="main-row"):
269
- # SIDEBAR (Controls & Context)
270
  with gr.Column(scale=1, min_width=300):
271
  gr.Markdown("<h4 style='font-family: monospace;'>⚙️ PARAMETERS</h4>")
272
  ticker_dd = gr.Dropdown(choices=["AAPL", "MSFT", "GOOGL", "AMZN"], value="AAPL", label="Asset Ticker")
@@ -277,9 +270,10 @@ with gr.Blocks(title="Quant Terminal | Stock ML", css=css, theme=gr.themes.Monoc
277
  consensus_panel = gr.HTML()
278
  context_panel = gr.HTML()
279
 
280
- # MAIN AREA (Charts)
281
  with gr.Column(scale=3):
282
  plot_chart = gr.Plot()
 
283
  btn_theme.click(fn=None, inputs=None, outputs=None, js=toggle_script)
284
  btn_predict.click(
285
  fn=generate_quant_dashboard,
@@ -287,7 +281,6 @@ with gr.Blocks(title="Quant Terminal | Stock ML", css=css, theme=gr.themes.Monoc
287
  outputs=[consensus_panel, context_panel, plot_chart]
288
  )
289
 
290
- # Load default data on start
291
  demo.load(
292
  fn=generate_quant_dashboard,
293
  inputs=[ticker_dd, model_dd],
 
45
  preds, last_close, last_date, _ = predict_horizons(ticker, model_name)
46
 
47
  # 2. Lấy dữ liệu OHLCV 90 ngày để vẽ Candlestick & tính toán Context
 
48
  end_dt = datetime.strptime(last_date, '%Y-%m-%d') + timedelta(days=1)
49
  start_dt = end_dt - timedelta(days=90)
50
 
 
80
  consensus_html = ""
81
  target_price = 0
82
 
83
+ # Hàm tính giá trung bình giữa các model
 
84
  def get_avg_price(h):
85
  if model_name == "Cả Hai":
86
  return (preds[h]["Linear Regression"]["pred_price"] + preds[h]["SVR"]["pred_price"]) / 2
 
94
  else:
95
  return preds[h][model_name]["pred_return"]
96
 
 
97
  target_1d = get_avg_price(1)
98
  target_7d = get_avg_price(7)
99
  target_21d = get_avg_price(21)
100
 
 
101
  ret_1d = get_avg_return(1)
102
  ret_7d = get_avg_return(7)
103
  ret_21d = get_avg_return(21)
104
 
105
+ # 1. LOGIC XÁC ĐỊNH TÍN HIỆU TỔNG THỂ
 
106
  if ret_21d > 0.02 and ret_7d > 0:
107
  signal_text = "STRONG BUY"
108
+ signal_color = "#00ff00"
109
  bg_color = "rgba(0, 255, 0, 0.1)"
110
  elif ret_21d > 0:
111
  signal_text = "ACCUMULATE"
112
+ signal_color = "#00cc66"
113
  bg_color = "rgba(0, 204, 102, 0.1)"
114
  elif ret_21d < -0.02 and ret_7d < 0:
115
  signal_text = "STRONG SELL"
116
+ signal_color = "#ff0000"
117
  bg_color = "rgba(255, 0, 0, 0.1)"
118
  else:
119
  signal_text = "REDUCE / SELL"
120
+ signal_color = "#ff6666"
121
  bg_color = "rgba(255, 102, 102, 0.1)"
122
 
 
123
  def fmt_ret(val):
124
+ color = "#00cc00" if val > 0 else "#ff4444" # Điều chỉnh màu xanh lá cây hơi tối xuống 1 chút cho chế độ nền trắng dễ nhìn
125
  sign = "+" if val > 0 else ""
126
  return f"<span style='color: {color}; font-weight: bold;'>{sign}{val*100:.2f}%</span>"
127
 
 
160
  </div>
161
  """
162
 
163
+ # 4. Market Context Panel
 
 
 
 
 
164
  context_html = f"""
165
  <div style="background: var(--background-fill-secondary); border: 1px solid var(--border-color-primary); padding: 15px; border-radius: 5px; font-family: monospace;">
166
  <p style="color: var(--body-text-color-subdued); margin:0 0 10px 0; font-size:12px;">LAST CLOSE: {last_date}</p>
 
173
  </div>
174
  """
175
 
176
+ # 5. Vẽ biểu đồ Plotly cấp độ Institutional (ĐÃ TỐI ƯU CHO LIGHT/DARK MODE)
177
  fig = make_subplots(rows=2, cols=1, shared_xaxes=True,
178
  vertical_spacing=0.03, row_heights=[0.75, 0.25],
179
  subplot_titles=(f"{ticker} - MULTI-HORIZON PROJECTIONS", "VOLUME"))
 
182
  fig.add_trace(go.Candlestick(
183
  x=df_ui['Date'], open=df_ui['Open'], high=df_ui['High'],
184
  low=df_ui['Low'], close=df_ui['Close'], name='Price',
185
+ increasing_line_color='#00cc00', decreasing_line_color='#ff3333' # Điều chỉnh màu nến tăng để không bị chói ở nền trắng
186
  ), row=1, col=1)
187
 
188
  # SMAs
 
190
  fig.add_trace(go.Scatter(x=df_ui['Date'], y=df_ui['SMA_50'], mode='lines', name='SMA 50', line=dict(color='#00bfff', width=1)), row=1, col=1)
191
 
192
  # Volume subplot
193
+ colors = ['#00cc00' if row['Close'] >= row['Open'] else '#ff3333' for _, row in df_ui.iterrows()]
194
  fig.add_trace(go.Bar(x=df_ui['Date'], y=df_ui['Volume'], marker_color=colors, name='Volume'), row=2, col=1)
195
+
196
  x_future = [base_date, dates_future[1], dates_future[7], dates_future[21]]
197
+
198
+ # Thêm điểm dự báo
199
  if model_name in ["Linear Regression", "Cả Hai"]:
200
  y_lr = [last_close, preds[1]["Linear Regression"]["pred_price"],
201
  preds[7]["Linear Regression"]["pred_price"], preds[21]["Linear Regression"]["pred_price"]]
 
206
  y_svr = [last_close, preds[1]["SVR"]["pred_price"],
207
  preds[7]["SVR"]["pred_price"], preds[21]["SVR"]["pred_price"]]
208
  fig.add_trace(go.Scatter(x=x_future, y=y_svr, mode='lines+markers', name='SVR Trajectory',
209
+ line=dict(color='#00cccc', dash='dot'), marker=dict(size=8, symbol='diamond')), row=1, col=1)
210
+
211
  upper_band = [last_close, target_1d + atr_val*np.sqrt(1), target_7d + atr_val*np.sqrt(7), target_21d + atr_val*np.sqrt(21)]
212
  lower_band = [last_close, target_1d - atr_val*np.sqrt(1), target_7d - atr_val*np.sqrt(7), target_21d - atr_val*np.sqrt(21)]
213
+
214
+ # Error Band (Đã đổi màu fillcolor thành xám trung tính độ trong suốt để tương thích tốt cả nền trắng và đen)
215
+ fig.add_trace(go.Scatter(x=x_future, y=upper_band, mode='lines', name='Risk Cone Upper', line=dict(color='rgba(0, 256, 0, 1)')), row=1, col=1)
216
+ fig.add_trace(go.Scatter(x=x_future, y=lower_band, mode='lines', fill='tonexty', fillcolor='rgba(128, 128, 128, 0.25)', name='Risk Cone Lower', line=dict(color='rgba(256, 0, 0, 0.6)')), row=1, col=1)
217
 
218
+ # --- TỐI ƯU LAYOUT CHO DARK / LIGHT MODE ---
219
  fig.update_layout(
220
  margin=dict(l=40, r=40, t=40, b=40),
221
  xaxis_rangeslider_visible=False,
222
+ legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1, font=dict(color="#888888")), # Chữ xám trung tính
223
+ font=dict(family="Courier New, monospace", color="#525252"), # Cả tiêu đề và nhãn đều xám trung tính
224
+ paper_bgcolor='rgba(0,0,0,0)', # Nền ngoài TRONG SUỐT
225
+ plot_bgcolor='rgba(0,0,0,0)' # Nền trong TRONG SUỐT
226
  )
227
 
228
+ # Thiết lập màu lưới trung tính chống chói
229
+ fig.update_xaxes(showgrid=True, gridwidth=1, gridcolor='rgba(128, 128, 128, 0.2)', linecolor='rgba(128, 128, 128, 0.3)', tickfont=dict(color="#888888"))
230
+ fig.update_yaxes(showgrid=True, gridwidth=1, gridcolor='rgba(128, 128, 128, 0.2)', linecolor='rgba(128, 128, 128, 0.3)', tickfont=dict(color="#888888"))
231
+
232
  return consensus_html, context_html, fig
233
 
234
+ # --- KIẾN TRÚC GRADIO GIAO DIỆN ---
235
  css = """
236
  .gradio-container { max-width: 1400px !important; }
237
  #main-row { margin-top: 20px; }
 
249
  with gr.Blocks(title="Quant Terminal | Stock ML", css=css, theme=gr.themes.Monochrome()) as demo:
250
  with gr.Row():
251
  with gr.Column(scale=9):
 
252
  gr.Markdown("""
253
  <div style="padding: 10px 0; border-bottom: 2px solid var(--border-color-primary);">
254
  <h1 style="margin: 0; font-family: monospace;">⚡ QUANTRONIC ML TERMINAL v2.0</h1>
 
256
  </div>
257
  """)
258
  with gr.Column(scale=1, min_width=150):
 
259
  btn_theme = gr.Button("🌓 Toggle Dark/Light", size="sm")
260
 
 
261
  with gr.Row(elem_id="main-row"):
262
+ # SIDEBAR
263
  with gr.Column(scale=1, min_width=300):
264
  gr.Markdown("<h4 style='font-family: monospace;'>⚙️ PARAMETERS</h4>")
265
  ticker_dd = gr.Dropdown(choices=["AAPL", "MSFT", "GOOGL", "AMZN"], value="AAPL", label="Asset Ticker")
 
270
  consensus_panel = gr.HTML()
271
  context_panel = gr.HTML()
272
 
273
+ # MAIN AREA
274
  with gr.Column(scale=3):
275
  plot_chart = gr.Plot()
276
+
277
  btn_theme.click(fn=None, inputs=None, outputs=None, js=toggle_script)
278
  btn_predict.click(
279
  fn=generate_quant_dashboard,
 
281
  outputs=[consensus_panel, context_panel, plot_chart]
282
  )
283
 
 
284
  demo.load(
285
  fn=generate_quant_dashboard,
286
  inputs=[ticker_dd, model_dd],