Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import pandas as pd | |
| from datetime import datetime | |
| from src.utils.visualization import create_revenue_profit_chart, create_return_chart | |
| from src.utils.excel_export import to_excel, generate_excel_filename | |
| def display_financial_analysis(income, balance, cashflow, ratio, symbol): | |
| """ | |
| Hiển thị tab phân tích tài chính | |
| Args: | |
| income: DataFrame chứa báo cáo kết quả kinh doanh | |
| balance: DataFrame chứa bảng cân đối kế toán | |
| cashflow: DataFrame chứa báo cáo lưu chuyển tiền tệ | |
| ratio: DataFrame chứa các chỉ số tài chính | |
| symbol: Mã cổ phiếu | |
| """ | |
| st.markdown('<div class="tab-content">', unsafe_allow_html=True) | |
| # Sidebar cho báo cáo tài chính | |
| period = st.selectbox("Chu kỳ báo cáo:", ["year", "quarter"], key="period") | |
| if all([income is not None, balance is not None, cashflow is not None, ratio is not None]): | |
| # Chỉ số tài chính quan trọng | |
| st.subheader("💎 Chỉ số Tài chính Quan trọng") | |
| # Xử lý MultiIndex columns | |
| try: | |
| # Lấy dữ liệu năm gần nhất | |
| if isinstance(ratio.columns, pd.MultiIndex): | |
| # Với MultiIndex columns | |
| year_col = ('Meta', 'Năm') | |
| latest_year = ratio[year_col].max() | |
| latest_ratio = ratio[ratio[year_col] == latest_year].iloc[0] | |
| col1, col2, col3, col4 = st.columns(4) | |
| with col1: | |
| try: | |
| pe = latest_ratio[('Chỉ tiêu định giá', 'P/E')] | |
| pb = latest_ratio[('Chỉ tiêu định giá', 'P/B')] | |
| except KeyError: | |
| pe = pb = 'N/A' | |
| st.markdown(f""" | |
| <div class="metric-card"> | |
| <h3>Định giá</h3> | |
| <p>P/E: {f'{pe:.2f}' if pe != 'N/A' and pd.notna(pe) else 'N/A'}</p> | |
| <p>P/B: {f'{pb:.2f}' if pb != 'N/A' and pd.notna(pb) else 'N/A'}</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with col2: | |
| try: | |
| roe = latest_ratio[('Chỉ tiêu khả năng sinh lợi', 'ROE (%)')] | |
| roa = latest_ratio[('Chỉ tiêu khả năng sinh lợi', 'ROA (%)')] | |
| except KeyError: | |
| roe = roa = 'N/A' | |
| st.markdown(f""" | |
| <div class="metric-card"> | |
| <h3>Sinh lời</h3> | |
| <p>ROE: {f'{roe:.2f}%' if roe != 'N/A' and pd.notna(roe) else 'N/A'}</p> | |
| <p>ROA: {f'{roa:.2f}%' if roa != 'N/A' and pd.notna(roa) else 'N/A'}</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with col3: | |
| try: | |
| current_ratio = latest_ratio[('Chỉ tiêu thanh khoản', 'Chỉ số thanh toán hiện thời')] | |
| debt_equity = latest_ratio[('Chỉ tiêu cơ cấu nguồn vốn', 'Nợ/VCSH')] | |
| except KeyError: | |
| current_ratio = debt_equity = 'N/A' | |
| st.markdown(f""" | |
| <div class="metric-card"> | |
| <h3>Tài chính</h3> | |
| <p>Thanh khoản: {f'{current_ratio:.2f}' if current_ratio != 'N/A' and pd.notna(current_ratio) else 'N/A'}</p> | |
| <p>Nợ/VCSH: {f'{debt_equity:.2f}' if debt_equity != 'N/A' and pd.notna(debt_equity) else 'N/A'}</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with col4: | |
| try: | |
| eps = latest_ratio[('Chỉ tiêu định giá', 'EPS (VND)')] | |
| bvps = latest_ratio[('Chỉ tiêu định giá', 'BVPS (VND)')] | |
| except KeyError: | |
| eps = bvps = 'N/A' | |
| st.markdown(f""" | |
| <div class="metric-card"> | |
| <h3>Trên cổ phiếu</h3> | |
| <p>EPS: {f'{eps:,.0f}' if eps != 'N/A' and pd.notna(eps) else 'N/A'}</p> | |
| <p>BVPS: {f'{bvps:,.0f}' if bvps != 'N/A' and pd.notna(bvps) else 'N/A'}</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| else: | |
| # Với cột thường | |
| year_col = None | |
| for col in ratio.columns: | |
| if 'Năm' in str(col): | |
| year_col = col | |
| break | |
| if year_col: | |
| latest_year = ratio[year_col].max() | |
| latest_ratio = ratio[ratio[year_col] == latest_year].iloc[0] | |
| st.info("Dữ liệu tài chính có sẵn nhưng cấu trúc cột khác mong đợi. Vui lòng kiểm tra lại.") | |
| else: | |
| st.warning("Không tìm thấy cột năm trong dữ liệu.") | |
| except Exception as e: | |
| st.error(f"Lỗi khi hiển thị chỉ số tài chính: {str(e)}") | |
| st.write("Dữ liệu ratio có sẵn:", ratio.head()) | |
| # Biểu đồ xu hướng tài chính | |
| st.subheader("📈 Xu hướng Tài chính") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| # Biểu đồ doanh thu và lợi nhuận | |
| try: | |
| # Tìm cột năm trong income | |
| year_col_income = None | |
| if isinstance(income.columns, pd.MultiIndex): | |
| year_col_income = ('Meta', 'Năm') if ('Meta', 'Năm') in income.columns else None | |
| else: | |
| for col in income.columns: | |
| if 'Năm' in str(col): | |
| year_col_income = col | |
| break | |
| # Tìm cột doanh thu và lợi nhuận | |
| revenue_col = None | |
| profit_col = None | |
| for col in income.columns: | |
| if isinstance(col, tuple): | |
| if 'doanh thu' in str(col).lower() and 'tỷ đồng' in str(col).lower(): | |
| revenue_col = col | |
| elif 'lợi nhuận sau thuế' in str(col).lower() and 'tỷ đồng' in str(col).lower(): | |
| profit_col = col | |
| else: | |
| if 'Doanh thu (Tỷ đồng)' in str(col): | |
| revenue_col = col | |
| elif 'Lợi nhuận sau thuế của Cổ đông công ty mẹ (Tỷ đồng)' in str(col): | |
| profit_col = col | |
| if year_col_income and revenue_col and profit_col: | |
| fig_revenue = create_revenue_profit_chart(income, year_col_income, revenue_col, profit_col) | |
| st.plotly_chart(fig_revenue, use_container_width=True) | |
| else: | |
| st.warning("Không tìm thấy đủ dữ liệu để hiển thị biểu đồ doanh thu và lợi nhuận.") | |
| except Exception as e: | |
| st.error(f"Không thể hiển thị biểu đồ doanh thu: {str(e)}") | |
| with col2: | |
| # Biểu đồ ROE, ROA | |
| try: | |
| # Tìm cột năm và ROE/ROA trong ratio | |
| year_col_ratio = None | |
| roe_col = None | |
| roa_col = None | |
| if isinstance(ratio.columns, pd.MultiIndex): | |
| year_col_ratio = ('Meta', 'Năm') if ('Meta', 'Năm') in ratio.columns else None | |
| roe_col = ('Chỉ tiêu khả năng sinh lợi', 'ROE (%)') if ('Chỉ tiêu khả năng sinh lợi', 'ROE (%)') in ratio.columns else None | |
| roa_col = ('Chỉ tiêu khả năng sinh lợi', 'ROA (%)') if ('Chỉ tiêu khả năng sinh lợi', 'ROA (%)') in ratio.columns else None | |
| else: | |
| for col in ratio.columns: | |
| if 'Năm' in str(col): | |
| year_col_ratio = col | |
| if 'ROE' in str(col): | |
| roe_col = col | |
| if 'ROA' in str(col): | |
| roa_col = col | |
| if year_col_ratio and roe_col and roa_col: | |
| fig_return = create_return_chart(ratio, year_col_ratio, roe_col, roa_col) | |
| st.plotly_chart(fig_return, use_container_width=True) | |
| else: | |
| st.warning("Không tìm thấy đủ dữ liệu để hiển thị biểu đồ ROE/ROA.") | |
| except Exception as e: | |
| st.error(f"Không thể hiển thị biểu đồ ROE/ROA: {str(e)}") | |
| # Bảng tài chính chi tiết | |
| st.subheader("📋 Báo cáo Tài chính Chi tiết") | |
| financial_tab1, financial_tab2, financial_tab3, financial_tab4 = st.tabs( | |
| ["Kết quả KD", "Bảng cân đối", "Lưu chuyển tiền", "Chỉ số tài chính"] | |
| ) | |
| with financial_tab1: | |
| st.dataframe(income, use_container_width=True) | |
| with financial_tab2: | |
| st.dataframe(balance, use_container_width=True) | |
| with financial_tab3: | |
| st.dataframe(cashflow, use_container_width=True) | |
| with financial_tab4: | |
| st.dataframe(ratio, use_container_width=True) | |
| # Tải xuống Excel | |
| st.subheader("📥 Tải xuống Báo cáo") | |
| excel_data = to_excel( | |
| [income, balance, cashflow, ratio], | |
| ['Kết quả KD', 'Bảng cân đối', 'Lưu chuyển tiền', 'Chỉ số tài chính'] | |
| ) | |
| st.download_button( | |
| label="📊 Tải xuống báo cáo Excel", | |
| data=excel_data, | |
| file_name=generate_excel_filename(symbol), | |
| mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" | |
| ) | |
| else: | |
| st.error("Không thể lấy dữ liệu tài chính. Vui lòng thử lại sau.") | |
| st.markdown('</div>', unsafe_allow_html=True) |