btmnngoc/dataexplorers2025dabaverse
0
1import streamlit as st2import pandas as pd3import numpy as np4import matplotlib.pyplot as plt5import plotly.graph_objects as go6from sklearn.preprocessing import MinMaxScaler7from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score8from tensorflow.keras.models import load_model9import xgboost as xgb10import joblib11import os12from datetime import datetime, timedelta13 14# Thiết lập trang Streamlit15st.set_page_config(16 page_title="Dự Báo Giá Cổ Phiếu FPT & CMG",17 page_icon="📈",18 layout="wide"19)20 21# Tiêu đề ứng dụng22st.title("📈 Hệ Thống Dự Báo Giá Cổ Phiếu FPT & CMG")23st.markdown("""24**Kết hợp phân tích kỹ thuật và cơ bản để dự báo giá cổ phiếu với đánh giá độ tin cậy**25""")26 27# Sidebar cho lựa chọn cổ phiếu và thông số28with st.sidebar:29 st.header("Cấu hình Dự Báo")30 stock_choice = st.selectbox("Chọn cổ phiếu", ["FPT", "CMG"])31 forecast_days = st.slider("Số ngày dự báo", 1, 30, 7)32 confidence_threshold = st.slider("Ngưỡng tin cậy tối thiểu (%)", 50, 95, 70)33 st.markdown("---")34 st.info("""35 Hệ thống sử dụng mô hình hybrid kết hợp:36 - LSTM cho phân tích chuỗi thời gian37 - XGBoost cho phân tích đặc trưng38 - Meta-model để kết hợp kết quả39 """)40 41# Hàm tải dữ liệu (giả lập - thay bằng dữ liệu thực tế của bạn)42@st.cache_data43def load_stock_data(stock_id):44 # Đây là phần giả lập - thay bằng code tải dữ liệu thực tế của bạn45 # Từ code gốc của bạn, bạn cần thay thế phần này với các file CSV thực tế46 47 # Tạo dữ liệu giả lập cho demo48 date_range = pd.date_range(end=datetime.today(), periods=365)49 prices = np.cumsum(np.random.normal(0.1, 2, 365)) + 10050 51 df = pd.DataFrame({52 'Date': date_range,53 'Closing Price': prices,54 'Total Volume': np.random.randint(100000, 500000, 365),55 56 'Volume_ratio': np.random.uniform(0.8, 1.2, 365),57 'Volatility': np.random.uniform(1, 5, 365),58 'Price_Momentum': np.random.normal(0, 2, 365),59 'Dividend_Event': np.random.choice([0, 1], 365, p=[0.95, 0.05]),60 'Meeting_Event': np.random.choice([0, 1], 365, p=[0.9, 0.1]),61 'ROE': np.random.uniform(10, 20, 365),62 'P/E': np.random.uniform(15, 25, 365),63 'EPS': np.random.uniform(5000, 8000, 365)64 })65 66 df['Return%'] = df['Closing Price'].pct_change() * 10067 df = df.fillna(0)68 69 return df70 71# Hàm tải mô hình72@st.cache_resource73def load_models(stock_id):74 # Trong thực tế, bạn cần thay bằng đường dẫn đến các model đã train75 # Đây chỉ là phần giả lập76 77 class DummyModel:78 def predict(self, X):79 return np.random.normal(0, 1, len(X))80 81 return {82 'lstm': DummyModel(),83 'xgb': DummyModel(),84 'meta': DummyModel()85 }86 87# Tải dữ liệu và mô hình88df = load_stock_data(stock_choice)89models = load_models(stock_choice)90 91# Tab chính92tab1, tab2, tab3 = st.tabs(["📊 Dữ Liệu & Phân Tích", "🔮 Dự Báo Giá", "📌 Đề Xuất Giao Dịch"])93 94with tab1:95 st.header(f"Phân Tích Cổ Phiếu {stock_choice}")96 97 col1, col2 = st.columns(2)98 99 with col1:100 st.subheader("Biểu Đồ Giá")101 fig = go.Figure()102 fig.add_trace(go.Scatter(x=df['Date'], y=df['Closing Price'], name='Giá đóng cửa', line=dict(color='blue')))103 104 fig.update_layout(height=400, xaxis_title='Ngày', yaxis_title='Giá')105 st.plotly_chart(fig, use_container_width=True)106 107 with col2:108 st.subheader("Chỉ Số Kỹ Thuật")109 110 # Tính RSI111 delta = df['Closing Price'].diff()112 gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()113 loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()114 rs = gain / loss115 rsi = 100 - (100 / (1 + rs))116 117 # Tính MACD118 ema12 = df['Closing Price'].ewm(span=12, adjust=False).mean()119 ema26 = df['Closing Price'].ewm(span=26, adjust=False).mean()120 macd = ema12 - ema26121 signal = macd.ewm(span=9, adjust=False).mean()122 123 # Hiển thị các chỉ số124 st.metric("RSI (14 ngày)", f"{rsi.iloc[-1]:.2f}", 125 "Mua quá" if rsi.iloc[-1] > 70 else "Bán quá" if rsi.iloc[-1] < 30 else "Bình thường")126 st.metric("MACD", f"{macd.iloc[-1]:.2f}", 127 "Tăng" if macd.iloc[-1] > signal.iloc[-1] else "Giảm")128 st.metric("Khối lượng giao dịch", f"{df['Total Volume'].iloc[-1]:,.0f}")129 130 # Phân tích xu hướng131 trend = "Tăng" if df['Closing Price'].iloc[-1] > df['Closing Price'].iloc[-5] else "Giảm"132 st.metric("Xu hướng ngắn hạn", trend)133 134with tab2:135 st.header(f"Dự Báo Giá {stock_choice}")136 137 # Chuẩn bị dữ liệu cho dự báo (giả lập)138 lookback = 7139 last_data = df.iloc[-lookback:][['Closing Price']].values140 scaler = MinMaxScaler()141 scaled_data = scaler.fit_transform(last_data)142 143 # Tạo dự báo (giả lập)144 forecast_dates = [df['Date'].iloc[-1] + timedelta(days=i) for i in range(1, forecast_days+1)]145 forecast_prices = []146 confidence_scores = []147 148 for i in range(forecast_days):149 # Trong thực tế, bạn sẽ sử dụng model.predict()150 pred = df['Closing Price'].iloc[-1] * (1 + np.random.normal(0.001, 0.02))151 forecast_prices.append(pred)152 153 # Tính độ tin cậy giả lập (dựa trên độ biến động gần đây)154 recent_volatility = df['Volatility'].iloc[-10:].mean()155 confidence = max(50, 100 - recent_volatility * 5 + np.random.normal(10, 5))156 confidence_scores.append(confidence)157 158 # Hiển thị kết quả dự báo159 col1, col2 = st.columns(2)160 161 with col1:162 st.subheader("Biểu Đồ Dự Báo")163 164 fig = go.Figure()165 fig.add_trace(go.Scatter(166 x=df['Date'].iloc[-30:], 167 y=df['Closing Price'].iloc[-30:], 168 name='Giá lịch sử',169 line=dict(color='blue')170 ))171 fig.add_trace(go.Scatter(172 x=forecast_dates,173 y=forecast_prices,174 name='Dự báo',175 line=dict(color='red', dash='dot')176 ))177 178 # Thêm vùng độ tin cậy179 for i, (date, price, conf) in enumerate(zip(forecast_dates, forecast_prices, confidence_scores)):180 color = 'green' if conf >= confidence_threshold else 'orange' if conf >= 60 else 'red'181 fig.add_shape(type="line",182 x0=date, y0=price*0.98, x1=date, y1=price*1.02,183 line=dict(color=color, width=2)184 )185 if i % 3 == 0: # Hiển thị nhãn cho một số ngày để tránh rối186 fig.add_annotation(x=date, y=price*1.03,187 text=f"{conf:.0f}%",188 showarrow=False,189 font=dict(size=10, color=color)190 )191 192 fig.update_layout(193 height=500,194 title=f"Dự báo giá {stock_choice} trong {forecast_days} ngày tới",195 xaxis_title="Ngày",196 yaxis_title="Giá",197 showlegend=True198 )199 st.plotly_chart(fig, use_container_width=True)200 201 with col2:202 st.subheader("Chi Tiết Dự Báo")203 204 # Tạo bảng dự báo205 forecast_df = pd.DataFrame({206 'Ngày': forecast_dates,207 'Giá dự báo': forecast_prices,208 'Độ tin cậy (%)': confidence_scores,209 'Biến động (%)': [(p/df['Closing Price'].iloc[-1]-1)*100 for p in forecast_prices]210 })211 212 # Định dạng bảng213 forecast_df['Giá dự báo'] = forecast_df['Giá dự báo'].apply(lambda x: f"{x:,.0f}")214 forecast_df['Biến động (%)'] = forecast_df['Biến động (%)'].apply(lambda x: f"{x:+.2f}%")215 forecast_df['Độ tin cậy (%)'] = forecast_df['Độ tin cậy (%)'].apply(lambda x: f"{x:.0f}%")216 217 # Hiển thị bảng với màu sắc theo độ tin cậy218 def color_confidence(val):219 val = float(val.strip('%'))220 color = 'green' if val >= confidence_threshold else 'orange' if val >= 60 else 'red'221 return f'background-color: {color}; color: white'222 223 st.dataframe(224 forecast_df.style.applymap(color_confidence, subset=['Độ tin cậy (%)']),225 hide_index=True,226 use_container_width=True227 )228 229 # Thống kê dự báo230 avg_confidence = np.mean(confidence_scores)231 max_change = max([abs(float(x.strip('%'))) for x in forecast_df['Biến động (%)']])232 233 st.metric("Độ tin cậy trung bình", f"{avg_confidence:.1f}%")234 st.metric("Biến động tối đa dự kiến", f"{max_change:.2f}%")235 236 # Đánh giá tổng quan237 if avg_confidence >= confidence_threshold:238 st.success("✅ Dự báo có độ tin cậy cao, có thể cân nhắc sử dụng")239 elif avg_confidence >= 60:240 st.warning("⚠️ Dự báo có độ tin cậy trung bình, cần thận trọng")241 else:242 st.error("❌ Dự báo có độ tin cậy thấp, không nên sử dụng")243 244with tab3:245 st.header(f"Đề Xuất Giao Dịch {stock_choice}")246 247 # Phân tích kỹ thuật để đưa ra đề xuất248 current_price = df['Closing Price'].iloc[-1]249 250 rsi_value = rsi.iloc[-1] if 'rsi' in locals() else 50 # Sử dụng RSI đã tính ở tab1251 252 # Tạo đề xuất253 recommendation = "Giữ"254 confidence = 70255 reasoning = []256 257 258 259 if rsi_value < 30:260 recommendation = "Mua mạnh" if recommendation == "Mua" else "Mua"261 confidence = min(95, confidence + 15)262 reasoning.append("RSI dưới 30 - cổ phiếu bị bán quá mức")263 elif rsi_value > 70:264 recommendation = "Bán mạnh" if recommendation == "Bán" else "Bán"265 confidence = min(95, confidence + 15)266 reasoning.append("RSI trên 70 - cổ phiếu mua quá mức")267 268 # Xem xét các sự kiện công ty269 if df['Dividend_Event'].iloc[-5:].sum() > 0:270 reasoning.append("Có sự kiện cổ tức gần đây - thường tạo biến động giá")271 272 if df['Meeting_Event'].iloc[-5:].sum() > 0:273 reasoning.append("Có sự kiện họp cổ đông gần đây - cần theo dõi thông tin")274 275 # Hiển thị đề xuất276 col1, col2 = st.columns([1, 3])277 278 with col1:279 st.subheader("Khuyến Nghị")280 281 if recommendation.startswith("Mua"):282 st.success(f"### {recommendation}")283 elif recommendation.startswith("Bán"):284 st.error(f"### {recommendation}")285 else:286 st.info(f"### {recommendation}")287 288 st.metric("Độ tin cậy", f"{confidence}%")289 st.metric("Giá hiện tại", f"{current_price:,.0f}")290 291 # Điểm vào lệnh và cắt lỗ đề xuất292 if recommendation.startswith("Mua"):293 entry = current_price * 0.99294 stop_loss = current_price * 0.95295 take_profit = current_price * 1.08296 elif recommendation.startswith("Bán"):297 entry = current_price * 1.01298 stop_loss = current_price * 1.05299 take_profit = current_price * 0.92300 else:301 entry = current_price302 stop_loss = current_price * 0.97303 take_profit = current_price * 1.03304 305 st.metric("Điểm vào lệnh", f"{entry:,.0f}")306 st.metric("Cắt lỗ", f"{stop_loss:,.0f}", delta=f"{(stop_loss/current_price-1)*100:+.1f}%")307 st.metric("Chốt lời", f"{take_profit:,.0f}", delta=f"{(take_profit/current_price-1)*100:+.1f}%")308 309 with col2:310 st.subheader("Phân Tích Chi Tiết")311 312 # Hiển thị lý do313 st.write("**Cơ sở đề xuất:**")314 for reason in reasoning:315 st.write(f"- {reason}")316 317 # Hiển thị các chỉ số quan trọng318 st.write("**Chỉ số quan trọng:**")319 cols = st.columns(4)320 with cols[0]:321 st.metric("P/E", f"{df['P/E'].iloc[-1]:.1f}")322 with cols[1]:323 st.metric("ROE", f"{df['ROE'].iloc[-1]:.1f}%")324 with cols[2]:325 st.metric("EPS", f"{df['EPS'].iloc[-1]:,.0f}")326 with cols[3]:327 st.metric("Volume Ratio", f"{df['Volume_ratio'].iloc[-1]:.2f}")328 329 # Cảnh báo rủi ro330 st.warning("**Cảnh báo rủi ro:**")331 st.write("""332 - Dự báo không đảm bảo chính xác 100%333 - Thị trường có thể biến động do yếu tố vĩ mô334 - Luôn sử dụng lệnh cắt lỗ để quản lý rủi ro335 - Cân nhắc đa dạng hóa danh mục đầu tư336 """)337 338# Footer339st.markdown("---")340st.markdown("""341**Hướng dẫn sử dụng:**3421. Chọn cổ phiếu và số ngày dự báo ở sidebar3432. Xem phân tích kỹ thuật và cơ bản ở tab đầu tiên3443. Kiểm tra dự báo giá và độ tin cậy ở tab thứ hai3454. Tham khảo đề xuất giao dịch ở tab cuối cùng346""")