
Phân tích báo cáo tài chính từ dữ liệu có cấu trúc truy xuất bởi Vnstock
Mục lục
Một phép tính đúng chưa chắc tạo ra một phép so sánh đúng. Hai con số có thể cùng tên nhưng khác phạm vi, khác kỳ, khác mức độ hợp nhất hoặc nằm ở hai cấp khác nhau trong báo cáo. Chương trình vẫn chạy, biểu đồ vẫn được vẽ, chỉ có ý nghĩa của kết quả là không còn chắc chắn.
Đây là lý do Vnstock dành nhiều nỗ lực xây dựng Taxonomy Engine — hệ thống phân loại báo cáo tài chính trong thư viện vnstock_data thuộc gói tài trợ của dự án. Trong bài này, tôi gọi ngắn gọn nó là bộ phân loại. Mục tiêu là giữ cho mỗi khoản mục có mã nhận diện, đơn vị và vị trí đủ rõ trước khi nó đi vào một phép so sánh hoặc một sản phẩm phân tích.
Bài viết tập trung vào góc nhìn thiết kế công cụ và xử lý dữ liệu: bộ phân loại giữ ý nghĩa của khoản mục thế nào, giúp nối dữ liệu ra sao và có thể trở thành nền cho những ứng dụng nào. Các lát cắt doanh nghiệp bên dưới cho thấy từng quyết định kỹ thuật được dùng vào việc gì.
Đây là bài thứ 2 trong loạt bài về dữ liệu báo cáo tài chính của Vnstock. Bài 1 chia sẻ hành trình đi từ tên cột thiếu ổn định tới một hệ thống phân loại chung. Bài này đi tiếp từ đầu ra đó: khi ý nghĩa và cấu trúc khoản mục đã được giữ lại, ta có thể làm gì với dữ liệu?
Dữ liệu có cấu trúc thay đổi điểm xuất phát của phân tích
Trong vnstock_data, bộ phân loại đưa dữ liệu từ mỗi nguồn về cùng một cách biểu diễn. Một khoản mục không chỉ còn giá trị, mà đi kèm mã id, tên hiển thị, vị trí trong báo cáo, đơn vị và cấp phân loại. Mỗi cột trả lời một câu hỏi thực tế:
| Câu hỏi cần kiểm soát | Thông tin hỗ trợ | Ý nghĩa trong phân tích |
|---|---|---|
| Đây có phải cùng một khoản mục? | id, name | Theo dõi đúng chỉ tiêu qua nhiều kỳ và nhiều nguồn |
| Con số thuộc kỳ nào và có cùng cơ sở thời gian? | period | Tránh trộn số thời điểm, số thời kỳ và số lũy kế |
| Hai giá trị có cùng quy mô đo lường? | unit | Tránh so sánh sai do đồng, nghìn đồng, tỷ lệ hay số lần |
| Dòng này là tổng hay thành phần? | order, level | Giữ cấu trúc báo cáo và hạn chế cộng trùng cha – con |
| Khoản mục có nằm ngoài bảng cân đối? | off_balance | Không trộn cam kết hoặc nghĩa vụ với tài sản trên bảng |
Định dạng bảng dài giữ các thông tin này bên cạnh giá trị. Khi cần vẽ biểu đồ hoặc đưa vào mô hình, người dùng vẫn có thể xoay sang bảng rộng; nhưng bảng rộng lúc này được tạo ra cho từng nhu cầu, không phải nơi duy nhất cất giữ ý nghĩa của khoản mục.
Ví dụ, truy vấn báo cáo năm của HPG được thực hiện trực tiếp từ lớp Fundamental. Năm dòng dữ liệu đầu và 5 dòng cuối trong kết quả trả về cho thấy bộ phân loại được thể hiện ngay trong đầu ra của thư viện:
Minh hoạ cách truy xuất dữ liệu BCTC từ vnstock_data
Cột id chứa ID dạng ngữ nghĩa được vnstock định danh, không phải mã tiêu chí riêng của nguồn truy xuất dữ liệu. Mã của từng nguồn được chuyển đổi về bộ id ngữ nghĩa tại bước chuẩn hoá dữ liệu của từng nguồn. Khi dữ liệu cuối cùng được thể hiện, người dùng làm việc với một id ổn định; cột name dùng các dấu ‣, •, ◦ để thể hiện khoản mục cha – con. Hai cột order và level giúp dựng lại đúng thứ tự và phân cấp, off_balance cho biết các khoản mục nào ngoài bảng cân đối kế toán còn unit giữ thông tin đơn vị của chỉ tiêu để hiểu đúng bản chất dữ liệu trước khi tính toán.
Một dữ liệu, ba cách trình bày
Trong giao diện Unified UI của vnstock_data, tham số format không chỉ giúp nhanh chóng đổi cách trình bày bảng dữ liệu. Nó được thiết kế để hỗ trợ ba cách làm việc khác nhau:
from vnstock_data import Fundamental
f = Fundamental()
equity = f.equity("HPG")
equity.balance_sheet(period="quarter", format="long")
Định dạng bảng dài mặc định
wide_df = equity.balance_sheet(period="quarter", format="wide")
Định dạng bảng rộng
time_series_df = equity.balance_sheet(period="quarter", format="time_series")
Định dạng chuỗi thời gian
Cụ thể:
| format | Cách sắp xếp | Phù hợp khi |
|---|---|---|
| long | Mỗi dòng là một cặp period – id; các cột name, order, level, off_balance, unit và value đi cùng nhau | Lọc khoản mục, kiểm tra phân cấp, ghép nhiều doanh nghiệp hoặc nhiều nguồn |
| wide | Mỗi id là một dòng; name, item, level, order và unit được giữ bên trái, còn các kỳ như 2025-Q1, 2025-Q2 trở thành cột | Đọc báo cáo theo chiều ngang và đặt nhiều kỳ cạnh nhau |
| time_series | Mỗi kỳ báo cáo period là một dòng; mỗi id trở thành một cột dữ liệu | Vẽ chuỗi thời gian, tính biến động hoặc đưa dữ liệu vào mô hình |
Ba định dạng không tạo ra ba bộ dữ liệu khác nhau. Chúng là ba cách nhìn trên cùng các ID ngữ nghĩa.
longgiữ được nhiều thông tin cấu trúc nhất;widethuận mắt khi đọc báo cáo;time_seriesthuận tay khi tính toán theo thời gian.
Chẳng hạn so sánh tăng trưởng của một nhóm khoản mục có thể bắt đầu từ time_series; đối chiếu nhiều kỳ trên màn hình dùng wide; còn lọc theo level, kiểm tra dòng cha – con hoặc ghép nhiều doanh nghiệp nên giữ ở long. Vì vậy, các ví dụ phía sau có thể bắt đầu từ định dạng phù hợp với câu hỏi thay vì tự xoay bảng lại từ đầu.
Bộ khung chung giảm lỗi nối, sai đơn vị và nhầm cấp khoản mục. Nó không lựa chọn mẫu số, nhóm so sánh hay cách diễn giải thay người dùng trong ngữ cảnh phân tích tài chính.
Sáu cách dùng cùng một đầu ra
Sáu phần tiếp theo dùng cùng đầu ra này cho sáu việc khác nhau: nối chuỗi thời gian, so sánh doanh nghiệp, tính tỷ trọng, nối ba báo cáo, giữ cấu trúc riêng của từng ngành và tạo bảng nghiên cứu nhiều mã. Ở mỗi phần, điều cần nhìn không chỉ là kết quả tính toán mà còn là cột nào của bộ phân loại giúp phép tính đó duy trì tính ổn định của thuật toán.
1. So sánh theo thời gian: đọc quan hệ thay vì một tốc độ tăng
Khi một khoản mục giữ id ổn định, chuỗi thời gian không bị gãy chỉ vì nguồn đổi tên hiển thị. Quan trọng hơn, nhiều id liên quan có thể được đặt cạnh nhau để đọc doanh thu cùng phải thu, tồn kho và dòng tiền thay vì nhìn một tốc độ tăng riêng lẻ.
Một lát cắt thực tế với HPG
Lấy dữ liệu năm 2024–2025 của HPG qua nguồn VCI và đặt năm khoản mục thuộc ba báo cáo cạnh nhau:
| id | Khoản mục | 2024 | 2025 | Thay đổi |
|---|---|---|---|---|
| IS_NET_REVENUE | Doanh thu thuần | 138,86 nghìn tỷ | 156,12 nghìn tỷ | +12,4% |
| IS_NET_PROFIT_AFTER_TAX | Lợi nhuận sau thuế | 12,02 nghìn tỷ | 15,51 nghìn tỷ | +29,1% |
| BS_TRADE_RECEIVABLES | Phải thu ngắn hạn của khách hàng | 4,35 nghìn tỷ | 10,97 nghìn tỷ | +152,1% |
| BS_INVENTORIES | Hàng tồn kho | 46,09 nghìn tỷ | 52,83 nghìn tỷ | +14,6% |
| CF_NET_CASH_FLOWS_FROM_OPERATING_ACTIVITIES | Lưu chuyển tiền thuần từ hoạt động kinh doanh | 6,61 nghìn tỷ | 17,37 nghìn tỷ | +162,8% |
Nếu chỉ nhìn phải thu tăng hơn 150%, người đọc rất dễ dừng ở một cảnh báo. Nhưng khi nối thêm kết quả kinh doanh và lưu chuyển tiền tệ, ta thấy dòng tiền kinh doanh cùng kỳ cũng tăng mạnh. Nhờ các khoản mục giữ nguyên id qua hai năm và giữa ba báo cáo, thay đổi của phải thu không còn đứng riêng lẻ.
Câu hỏi lúc này cụ thể hơn: phần tăng của phải thu đến từ đâu, có lặp lại qua nhiều kỳ không và có khớp với thuyết minh doanh nghiệp công bố không. Bộ phân loại tạo ra đường dẫn từ một biến động tới những dòng cần kiểm tra tiếp.
Các số liệu được làm tròn để dễ đọc. Trọng tâm của ví dụ là cách nối khoản mục, lần theo quan hệ giữa các báo cáo và mở ra câu hỏi kiểm tra tiếp theo.
Dữ liệu quý trong ví dụ đã là số riêng từng kỳ
Bảng cân đối kế toán vẫn phản ánh số tại cuối mỗi quý. Với báo cáo kết quả kinh doanh và lưu chuyển tiền tệ, kết nối VCI đang dùng trong bài trả về số riêng của từng quý khi gọi period="quarter"; người dùng không cần lấy Q2 trừ Q1 để suy ra quý II.
Có thể kiểm tra trực tiếp trên HPG năm 2025 bằng hai ID thuộc hai báo cáo khác nhau:
| id | period | value |
|---|---|---|
| IS_NET_REVENUE | 2025-Q1 | 3.7621674553578e+13 |
| IS_NET_REVENUE | 2025-Q2 | 3.5910519111209e+13 |
| IS_NET_REVENUE | 2025-Q3 | 3.6407416403924e+13 |
| IS_NET_REVENUE | 2025-Q4 | 4.6176484549771e+13 |
| CF_NET_CASH_FLOWS_FROM_OPERATING_ACTIVITIES | 2025-Q1 | -2.777940446480e+12 |
| CF_NET_CASH_FLOWS_FROM_OPERATING_ACTIVITIES | 2025-Q2 | 3.285102529955e+12 |
| CF_NET_CASH_FLOWS_FROM_OPERATING_ACTIVITIES | 2025-Q3 | 8.294398995676e+12 |
| CF_NET_CASH_FLOWS_FROM_OPERATING_ACTIVITIES | 2025-Q4 | 8.564297977440e+12 |
Bốn quý của IS_NET_REVENUE cộng lại đúng 156116094618482, bằng giá trị năm 2025. Tương tự, bốn quý của CF_NET_CASH_FLOWS_FROM_OPERATING_ACTIVITIES cộng lại đúng 17365859056591, cũng bằng số năm. Đây là cách kiểm tra đơn giản nhưng đủ để xác nhận chuỗi quý trong ví dụ không phải chuỗi lũy kế.
Lưu ý về số lũy kế vẫn cần thiết khi đọc trực tiếp báo cáo PDF hoặc đưa thêm một nguồn mới vào hệ thống, vì cách trình bày tài liệu gốc có thể khác nhau.
2. So sánh ngang: từ hai doanh nghiệp tới cả nhóm ngành
So sánh ngang là đặt nhiều doanh nghiệp tại cùng một kỳ và dùng cùng cách tính. Để minh hoạ, câu hỏi ở đây rất cụ thể: tại cuối năm 2025, phải thu ngắn hạn và hàng tồn kho của HPG, VNM tương đương bao nhiêu phần doanh thu năm?
Ba khoản mục đầu vào được chọn bằng ID ngữ nghĩa, không dò theo tên cột:
| Tên tiêu chí | ID ngữ nghĩa | Vai trò trong phép tính |
|---|---|---|
| Doanh thu thuần | IS_NET_REVENUE | Mẫu số |
| Các khoản phải thu ngắn hạn | BS_SHORT_TERM_RECEIVABLES | Tử số thứ nhất |
| Hàng tồn kho | BS_INVENTORIES | Tử số thứ hai |
Đầu ra năm 2025 kết nối từ VCI cho thấy các giá trị thô sau:
| Mã | id | value |
|---|---|---|
| HPG | IS_NET_REVENUE | 156116094618482 |
| HPG | BS_SHORT_TERM_RECEIVABLES | 15042323117690 |
| HPG | BS_INVENTORIES | 52828227344442 |
| VNM | IS_NET_REVENUE | 63645886756227 |
| VNM | BS_SHORT_TERM_RECEIVABLES | 6027719081073 |
| VNM | BS_INVENTORIES | 6839279842936 |
Từ đó, phép tính được viết rõ bằng chính các ID:
- Phải thu trên doanh thu = BS_SHORT_TERM_RECEIVABLES / IS_NET_REVENUE.
- Tồn kho trên doanh thu = BS_INVENTORIES / IS_NET_REVENUE.
| Mã | Phải thu / doanh thu | Tồn kho / doanh thu |
|---|---|---|
| HPG | 9,6% | 33,8% |
| VNM | 9,5% | 10,7% |
Ví dụ này cho thấy vai trò của bộ phân loại khá rõ ràng: cùng một công thức có thể chạy cho cả hai doanh nghiệp vì ba id đã giữ cùng ý nghĩa. Kết quả cũng dễ đọc hơn: tỷ lệ phải thu gần như tương đương, trong khi tỷ lệ tồn kho khác đáng kể.
Sự khác biệt đó chưa phải kết luận doanh nghiệp nào tốt hơn. HPG và VNM có mô hình kinh doanh khác nhau, còn tử số là số tại cuối năm trong khi doanh thu là số phát sinh cả năm. Lát cắt này phù hợp để tìm điểm cần đọc tiếp; nếu tính vòng quay chính xác hơn, có thể dùng tồn kho hoặc phải thu bình quân đầu kỳ và cuối kỳ.
Khi mở rộng từ hai mã sang một nhóm lớn hơn, quy trình vẫn giữ nguyên: ghép dữ liệu long, lọc ba id, tính cùng công thức rồi mới chia nhóm ngành để so sánh. Nếu một doanh nghiệp thiếu một ID, giá trị đó cần giữ là thiếu dữ liệu hoặc không áp dụng, không tự động đổi thành 0.
3. So sánh dọc: tỷ trọng cho biết tài sản đang nằm ở đâu
Phép chia để tính tỷ trọng khá đơn giản. Phần dễ sai là chọn một dòng tổng làm mẫu số nhưng lại cộng lẫn dòng cha và dòng con ở tử số. Với đầu ra long của HPG, có thể lấy một lát cắt và sắp lại bằng order:
asset_ids = [
"BS_SHORT_TERM_ASSETS",
"BS_CASH_AND_PRECIOUS_METALS",
"BS_CASH",
"BS_SHORT_TERM_RECEIVABLES",
"BS_TRADE_RECEIVABLES",
"BS_INVENTORIES",
"BS_FIXED_ASSETS",
"BS_TOTAL_ASSETS",
]
asset_view = (
report.loc[
(report["period"] == "2025") & report["id"].isin(asset_ids),
["id", "name", "order", "level", "unit", "value"],
]
.sort_values("order")
)Kết quả giữ nguyên giá trị và cách lồng cấp mà thư viện trả về:
| id | name | order | level | unit | value |
|---|---|---|---|---|---|
| BS_SHORT_TERM_ASSETS | TÀI SẢN NGẮN HẠN | 1 | 1 | VNĐ | 1.036594e+14 |
| BS_CASH_AND_PRECIOUS_METALS | Tiền và các khoản tương đương tiền | 2 | 1 | VNĐ | 8.300890e+12 |
| BS_CASH | ‣ Tiền | 3 | 2 | VNĐ | 4.602048e+12 |
| BS_SHORT_TERM_RECEIVABLES | Các khoản phải thu ngắn hạn | 9 | 1 | VNĐ | 1.504232e+13 |
| BS_TRADE_RECEIVABLES | ‣ Phải thu ngắn hạn của khách hàng | 10 | 2 | VNĐ | 1.097177e+13 |
| BS_INVENTORIES | Hàng tồn kho | 18 | 1 | VNĐ | 5.282823e+13 |
| BS_FIXED_ASSETS | Tài sản cố định | 34 | 1 | VNĐ | 1.336081e+14 |
| BS_TOTAL_ASSETS | TỔNG CỘNG TÀI SẢN | 63 | 1 | VNĐ | 2.578992e+14 |
Hai cặp dòng đầu cho thấy ngay rủi ro cộng trùng: BS_CASH là phần chi tiết của BS_CASH_AND_PRECIOUS_METALS; BS_TRADE_RECEIVABLES là phần chi tiết của BS_SHORT_TERM_RECEIVABLES. Dấu ‣ trong name cùng với level=2 giúp nhận ra quan hệ này khi đọc bảng. Vì vậy, để minh hoạ tỷ trọng, tôi chọn bốn id cấp trên không chồng lấn và chia từng giá trị cho BS_TOTAL_ASSETS:
values = asset_view.set_index("id")["value"]
selected_ids = [
"BS_CASH_AND_PRECIOUS_METALS",
"BS_SHORT_TERM_RECEIVABLES",
"BS_INVENTORIES",
"BS_FIXED_ASSETS",
]
asset_share = values[selected_ids] / values["BS_TOTAL_ASSETS"]Tổng tài sản HPG cuối năm 2025 là 2.578992e+14 đồng. Sau khi tính, bốn tỷ trọng được trình bày gọn hơn để đọc:
| id | Khoản mục | Giá trị | Tỷ trọng tổng tài sản |
|---|---|---|---|
| BS_CASH_AND_PRECIOUS_METALS | Tiền và các khoản tương đương tiền | 8,30 nghìn tỷ | 3,2% |
| BS_SHORT_TERM_RECEIVABLES | Các khoản phải thu ngắn hạn | 15,04 nghìn tỷ | 5,8% |
| BS_INVENTORIES | Hàng tồn kho | 52,83 nghìn tỷ | 20,5% |
| BS_FIXED_ASSETS | Tài sản cố định | 133,61 nghìn tỷ | 51,8% |
Điều bộ phân loại bổ sung không phải phép chia, mà là khả năng chọn đúng tử số và mẫu số theo id, đồng thời giữ đủ name, order và level để đối chiếu lại quyết định đó.
Nếu cộng cơ học tất cả các dòng thuộc phía tài sản, kết quả lại lên tới khoảng 1.165,20 nghìn tỷ đồng, cao hơn 4,5 lần vì dòng tổng và dòng chi tiết cùng xuất hiện.
level và order giúp đọc lại bố cục, nhưng không có nghĩa cứ chọn cùng một level là có thể cộng. Khi tính tỷ trọng, cách chắc chắn hơn là xác định trước một tập id không chồng lấn, chẳng hạn tiền, phải thu, tồn kho và các nhóm tài sản dài hạn phù hợp với câu hỏi đang đặt ra.
Không phải mẫu số nào cũng dùng được
Doanh thu thường được dùng làm mẫu số cho báo cáo kết quả kinh doanh, nhưng có thể mất ý nghĩa khi bằng 0, âm hoặc mang bản chất khác ở công ty tài chính. Với ngân hàng, tổng thu nhập hoạt động hoặc tài sản sinh lãi có thể phù hợp hơn. Cấu trúc dữ liệu giúp truy vấn đúng khoản mục; nó không lựa chọn mẫu số thay người dùng.
4. Nối ba báo cáo để kiểm tra một biến động
Bảng cân đối cho biết tài sản và nguồn tài trợ tại một thời điểm; báo cáo kết quả kinh doanh ghi nhận hoạt động trong kỳ; báo cáo lưu chuyển tiền tệ cho thấy kết quả đó chuyển thành tiền ra sao. Mã id ổn định cho phép đặt ba lát cắt này cạnh nhau thay vì đọc từng báo cáo riêng lẻ.
Một mâu thuẫn không mặc nhiên là lỗi. Lợi nhuận tăng trong khi dòng tiền âm có thể đến từ mùa vụ, mở rộng tín dụng cho khách hàng hoặc doanh thu chưa thu tiền. Điều đáng làm là xác định nguyên nhân và xem nó có lặp lại hay không.
Dữ liệu HPG năm 2025 từ kết nối VCI cho thấy rõ hơn cách ba báo cáo nối với nhau:
| Báo cáo | id | Dòng dữ liệu liên quan |
|---|---|---|
| Kết quả kinh doanh | IS_NET_PROFIT_AFTER_TAX | Lợi nhuận sau thuế 15,51 nghìn tỷ đồng |
| Bảng cân đối | BS_SHORT_TERM_RECEIVABLES, BS_INVENTORIES | Phải thu ngắn hạn tăng từ 7,65 lên 15,04 nghìn tỷ; tồn kho tăng từ 46,09 lên 52,83 nghìn tỷ |
| Lưu chuyển tiền tệ | CF_INCREASE_DECREASE_IN_RECEIVABLES, CF_INCREASE_DECREASE_IN_INVENTORY, CF_NET_CASH_FLOWS_FROM_OPERATING_ACTIVITIES | Tăng phải thu làm giảm 8,74 nghìn tỷ và tăng tồn kho làm giảm 7,24 nghìn tỷ trong phần điều chỉnh vốn lưu động; dòng tiền kinh doanh cuối cùng đạt 17,37 nghìn tỷ |
Các con số không được thiết kế để khớp một-một bằng phép trừ đơn giản, nhưng bộ phân loại giúp truy từ biến động trên bảng cân đối sang đúng dòng điều chỉnh trong lưu chuyển tiền tệ. Đây là nền để giao diện hoặc trợ lý AI dẫn người dùng tới chỗ cần đọc tiếp, thay vì chỉ đặt lợi nhuận cạnh dòng tiền rồi dừng lại.
Khung liên kết này có thể tái sử dụng cho nhiều quan hệ khác: biên lợi nhuận với khoản một lần, nợ vay với tài sản hình thành hoặc vốn chủ sở hữu với hiệu suất vốn. Phần công cụ cần làm tốt là đưa đúng các dòng liên quan về cùng ngữ cảnh để câu hỏi tiếp theo không bắt đầu từ việc dò cột.
5. So sánh cùng ngành: giữ phần chung nhưng không xoá phần riêng
Khác biệt giữa các ngành không nên chỉ nằm trong phần diễn giải. Nó cần được thể hiện ngay từ tập ID ngữ nghĩa dùng để lấy dữ liệu. Bảng dưới đây đặt tên tiêu chí cạnh đúng ID mà vnstock_data trả về, đồng thời cho biết cần tìm nó ở báo cáo nào:
| Mô hình doanh nghiệp | Tên tiêu chí | ID ngữ nghĩa | Dữ liệu nằm ở |
|---|---|---|---|
| Doanh nghiệp thông thường | Doanh thu thuần | IS_NET_REVENUE | Kết quả kinh doanh |
| Phải thu ngắn hạn | BS_SHORT_TERM_RECEIVABLES | Bảng cân đối | |
| Hàng tồn kho | BS_INVENTORIES | Bảng cân đối | |
| Tài sản cố định | BS_FIXED_ASSETS | Bảng cân đối | |
| Dòng tiền kinh doanh | CF_NET_CASH_FLOWS_FROM_OPERATING_ACTIVITIES | Lưu chuyển tiền tệ | |
| Ngân hàng | Cho vay khách hàng | BS_LOANS_TO_CUSTOMERS | Bảng cân đối |
| Tiền gửi khách hàng | BS_CUSTOMER_DEPOSITS | Bảng cân đối | |
| Thu nhập lãi thuần | IS_NET_INTEREST_INCOME | Kết quả kinh doanh | |
| Chi phí dự phòng rủi ro tín dụng | IS_PROVISION_FOR_CREDIT_LOSSES | Kết quả kinh doanh | |
| Công ty chứng khoán | Doanh thu môi giới | IS_REVENUE_FROM_BROKERAGE_SERVICES | Kết quả kinh doanh |
| Tài sản tài chính FVTPL | BS_FVTPL_FINANCIAL_ASSETS | Bảng cân đối | |
| Cho vay ký quỹ | NT_BS_MARGIN_LOAN | Thuyết minh | |
| Tiền gửi giao dịch của nhà đầu tư | BS_OFF_INVESTOR_TRADING_DEPOSITS | Ngoài bảng | |
| Bảo hiểm phi nhân thọ | Phí bảo hiểm gốc | IS_GROSS_WRITTEN_PREMIUM | Kết quả kinh doanh |
| Bồi thường thuộc trách nhiệm giữ lại | IS_RETAINED_CLAIMS | Kết quả kinh doanh | |
| Tài sản tái bảo hiểm | BS_REINSURANCE_ASSETS | Bảng cân đối | |
| Dự phòng nghiệp vụ bảo hiểm | BS_INSURANCE_RESERVES | Bảng cân đối |
Tiền tố của ID cũng giúp định tuyến truy vấn: BS_ thuộc bảng cân đối, IS_ thuộc kết quả kinh doanh, CF_ thuộc lưu chuyển tiền tệ và NT_ thuộc thuyết minh. Riêng các dòng BS_OFF_..., cột off_balance cho biết đây là khoản mục ngoài bảng. Nhờ vậy, danh sách trên có thể chuyển thẳng thành bộ lọc trong code thay vì phải dò tên tiếng Việt.
Đây không phải một bộ chỉ số chấm điểm có thể áp cho mọi doanh nghiệp. Nó cho thấy cách bộ phân loại gắn mỗi mô hình kinh doanh với đúng nhóm khoản mục: doanh nghiệp sản xuất cần theo dõi vốn lưu động, ngân hàng cần nối tín dụng với huy động và dự phòng, công ty chứng khoán cần tách tài sản của công ty với tài sản hoặc tiền của khách hàng, còn doanh nghiệp bảo hiểm cần đọc phí, bồi thường, tái bảo hiểm và dự phòng cùng nhau.
ROA có thể dùng cho nhiều ngành, nhưng tài sản tạo ra ROA của ngân hàng khác xa doanh nghiệp bán lẻ. Nợ phải trả cao có thể là tín hiệu rủi ro với doanh nghiệp sản xuất, trong khi tiền gửi khách hàng là nguồn vốn vận hành cốt lõi của ngân hàng.
Một bộ phân loại dùng cho nhiều ngành cần làm được hai việc cùng lúc:
- Đưa các khoản mục thực sự cùng bản chất về một bộ
idchung. - Giữ thêm những
idriêng quyết định mô hình kinh doanh của từng ngành.
Nếu chỉ làm việc thứ nhất, dữ liệu trở nên đồng nhất nhưng nghèo ý nghĩa. Nếu chỉ làm việc thứ hai, người dùng lại phải viết bốn hệ thống phân tích tách biệt.
Khi phần riêng của ngành trở thành khóa so sánh
Với dữ liệu VCB, BID và CTG năm 2025 từ nguồn dữ liệu VCI, mã BS_LOANS_TO_CUSTOMERS cùng chỉ khoản cho vay khách hàng sau dự phòng, còn BS_CUSTOMER_DEPOSITS cùng chỉ tiền gửi khách hàng. Kết hợp với BS_TOTAL_ASSETS và IS_NET_PROFIT_AFTER_TAX, bốn mã id này tạo thành một lát cắt dành riêng cho ngân hàng:
| Mã | Tổng tài sản | Cho vay khách hàng | Tiền gửi khách hàng | Lợi nhuận sau thuế |
|---|---|---|---|---|
| VCB | 2.442,28 nghìn tỷ | 1.648,55 nghìn tỷ | 1.672,54 nghìn tỷ | 35,20 nghìn tỷ |
| BID | 3.330,83 nghìn tỷ | 2.338,01 nghìn tỷ | 2.222,99 nghìn tỷ | 30,43 nghìn tỷ |
| CTG | 2.767,70 nghìn tỷ | 1.957,46 nghìn tỷ | 1.793,73 nghìn tỷ | 34,87 nghìn tỷ |
Nếu chỉ giữ những cột chung cho mọi doanh nghiệp, hai khoản mục quyết định cấu trúc ngân hàng này sẽ biến mất. Nếu giữ nguyên tên từ từng nguồn, phép ghép lại dễ vỡ. Bộ phân loại dùng một bộ id chung cho từng loại hình doanh nghiệp: đủ ổn định để so sánh, nhưng không làm phẳng đặc trưng ngành.
VCI, MAS và KBS có độ chi tiết khác nhau
Ba nguồn đều có thể đi qua bộ phân loại, nhưng phạm vi dữ liệu ban đầu không giống nhau. Cấu hình ánh xạ trong mã nguồn vnstock_data tại thời điểm biên tập cho thấy sự khác biệt khá rõ:
| Nhóm dữ liệu | Tổng số id | VCI | MAS | KBS |
|---|---|---|---|---|
| Bảng cân đối kế toán | 258 | 252 | 141 | 178 |
| Báo cáo kết quả kinh doanh | 160 | 160 | 120 | 103 |
| Báo cáo lưu chuyển tiền tệ | 194 | 194 | 31 | 57 |
| Thuyết minh | 1.087 | 1.083 | 0 | 0 |
| Tỷ số tài chính | 60 | 51 | 12 | 8 |
Đây là số lượng mã id chung đã có ánh xạ, không phải cam kết mọi khoản mục đều có giá trị ở mọi doanh nghiệp và mọi kỳ. VCI hiện có phạm vi rộng nhất và là nguồn duy nhất trong ba nguồn có nhóm dữ liệu trong thuyết minh BCTC. MAS có nhiều ánh xạ ở bảng cân đối và kết quả kinh doanh nhưng mỏng hơn ở lưu chuyển tiền tệ; KBS nằm giữa hai nguồn ở một số nhóm báo cáo.
Điểm đáng chú ý không phải nguồn nào có nhiều dòng hơn, mà là phần nào đã đủ cơ sở để ghép về cùng nghĩa và phần nào vẫn cần giữ riêng. Bộ phân loại làm lộ ranh giới đó thay vì che nó bằng những tên cột trông giống nhau.
6. Tương quan và nghiên cứu định lượng: mở rộng mẫu mà không mất nghĩa
Định dạng dài cho phép ghép nhiều doanh nghiệp, nhiều kỳ và nhiều báo cáo thành một bảng dữ liệu. Với cấu hình nguồn mặc định kết nối VCI hiện tại, ví dụ dưới đây lấy ba mã trong VN100 và giữ lại doanh thu thuần, lợi nhuận sau thuế cùng dòng tiền kinh doanh:
import pandas as pd
from vnstock_data import Fundamental
symbols = ["HPG", "VNM", "FPT"]
frames = []
for symbol in symbols:
equity = Fundamental().equity(symbol)
for report in ("income_statement", "cash_flow"):
data = getattr(equity, report)(
period="year", format="long", drop_empty=True
)
frames.append(data.assign(symbol=symbol, report=report))
panel = pd.concat(frames, ignore_index=True)
panel = panel[
panel["id"].isin([
"IS_NET_REVENUE",
"IS_NET_PROFIT_AFTER_TAX",
"CF_NET_CASH_FLOWS_FROM_OPERATING_ACTIVITIES",
])
]Điểm quan trọng là mã không cần đổi tên cột cho từng doanh nghiệp. Khóa symbol + period + report + id đủ để mở rộng danh sách lên VN100, còn name, order, level, unit và off_balance tiếp tục đi cùng dữ liệu khi cần giải thích hoặc kiểm tra cấu trúc. Nếu dùng tên hiển thị làm khóa, chỉ một thay đổi cách viết từ nguồn cũng có thể tách một chuỗi thành hai biến khác nhau.
Bộ phân loại giúp các cột trong bảng giữ đúng ý nghĩa, nhưng không tự chọn mẫu nghiên cứu hay giải thích kết quả. Tương quan cao có thể chỉ phản ánh quy mô, đặc điểm ngành hoặc một chu kỳ chung; kết quả vẫn cần được kiểm tra ngoài mẫu và trong nhóm so sánh phù hợp.
Thời điểm dữ liệu cũng quan trọng không kém giá trị. Nếu mô hình dùng báo cáo quý I từ ngày 31/03 trong khi doanh nghiệp công bố cuối tháng 4, nó đã nhìn thấy tương lai dù công thức không có lỗi. Thiên lệch sống sót và việc thử nhiều chỉ tiêu rồi chỉ giữ kết quả đẹp nhất cũng không biến mất nhờ dữ liệu đã được phân loại.
Từ dữ liệu tới một câu hỏi có thể kiểm tra
Ở phạm vi của công cụ truy xuất dữ liệu, một luồng sử dụng vừa đủ gồm bốn bước:
- Xác định loại doanh nghiệp, kỳ năm hay quý, báo cáo hợp nhất hay riêng lẻ.
- Chọn một tập
idphục vụ câu hỏi cụ thể, không lấy hoặc cộng cả bảng theo quán tính. - Đặt các khoản mục liên quan cạnh nhau qua nhiều kỳ, nhiều báo cáo hoặc nhóm doanh nghiệp phù hợp.
- Quay lại báo cáo và thuyết minh khi xuất hiện chênh lệch đáng chú ý; nếu dùng dữ liệu lịch sử, lưu thêm ngày công bố và phiên bản dữ liệu.
Luồng này đặt bộ phân loại vào đúng vai trò: giảm công sức nối và làm sạch để người dùng dành thời gian cho câu hỏi, kiểm tra nguồn và giả định của chính mình.
Cùng một đầu ra, nhiều cách dùng
Khi mã id và các thông tin đi kèm được dùng thống nhất, đầu ra không chỉ phục vụ một bảng phân tích. Nó có thể trở thành phần dùng chung cho nhiều tính năng khác nhau.
Báo cáo tài chính tương tác
order và level cho phép giao diện dựng lại bố cục, thu gọn hoặc mở rộng từng nhóm khoản mục. id làm khóa kỹ thuật, còn name phục vụ hiển thị. Khi nguồn đổi tên một dòng, giao diện không nhất thiết phải đổi theo nếu bản chất của khoản mục vẫn được xác định.
Bộ lọc và xếp hạng doanh nghiệp
Các yếu tố về tăng trưởng, dòng tiền, đòn bẩy hoặc hiệu quả có thể được tính trên cùng tập id. Phần đặc thù được giới hạn theo loại doanh nghiệp thay vì rải điều kiện xử lý khắp mã nguồn. Nhờ đó, một bộ lọc có thể mở rộng từ vài mã sang VN100 mà vẫn biết chỉ tiêu nào được phép so sánh và chỉ tiêu nào không áp dụng.
Cảnh báo quan hệ bất thường
Hệ thống có thể theo dõi quan hệ giữa doanh thu với phải thu, lợi nhuận với dòng tiền, dư nợ với dự phòng hoặc doanh thu phí với dự phòng nghiệp vụ. Cảnh báo khi đó bám vào một nhóm khoản mục liên quan thay vì một ngưỡng đơn lẻ. Nó gợi ý nơi cần đọc tiếp, không cố đóng vai trò kết luận.
Hồ sơ nghiên cứu cho trợ lý AI
Trợ lý AI cần biết một con số là tổng hay chi tiết, thuộc kỳ nào, đơn vị gì và áp dụng cho ngành nào. Các cột thông tin đi kèm giúp nó dựng hồ sơ theo cùng một cấu trúc, dẫn lại nguồn và biết lúc nào phải quay lại thuyết minh.
Giá trị không nằm ở việc để trợ lý AI tự kết luận. Giá trị nằm ở việc giảm nguy cơ nó so sánh hai dòng chỉ vì tên trông giống nhau.
Những giới hạn cần kiểm soát trước khi phân tích
Bộ phân loại giải quyết nhiều lỗi ở đầu vào, nhưng không biến đầu ra thành dữ liệu có thể dùng mà không kiểm tra. Tôi thường giữ một danh sách ngắn trước khi xây phép tính:
| Điểm cần kiểm soát | Cách xử lý thực tế |
|---|---|
| Dòng tổng và dòng chi tiết cùng tồn tại | Chọn trước tập id không chồng lấn; không cộng toàn bộ value |
| id chỉ nhận diện khoản mục | Giữ thêm mã chứng khoán, kỳ, loại báo cáo và phạm vi hợp nhất hoặc riêng lẻ |
| Dòng trống có nhiều ý nghĩa | Phân biệt không phát sinh, không áp dụng và nguồn chưa trả về; không tự điền 0 |
| Đơn vị có thể là tiền, tỷ lệ, số lần hoặc số lượng | Kiểm tra unit và quy mô triệu hay tỷ VNĐ trước khi ghép hoặc tính toán |
| Bảng rộng dễ làm rơi ngữ cảnh | Giữ bảng dài làm bản ghi chuẩn, chỉ xoay rộng cho từng nhu cầu |
| period không phải ngày công bố | Lưu thời điểm thông tin có thể được biết đến nếu làm nghiên cứu lịch sử |
| Tỷ lệ cùng tên có thể khác cách tính | Với chỉ tiêu quan trọng, ưu tiên tính lại từ khoản mục gốc và ghi rõ công thức |
| Bảng ánh xạ có thể được cập nhật | Lưu phiên bản thư viện, nguồn kết nối và ngày trích xuất để có thể tái lập |
ID chung giúp giảm lỗi nối và sai khác tên gọi. Nó không chứng minh mọi giá trị từ nguồn kết nối đều đúng, đầy đủ hoặc cập nhật cùng thời điểm. Khoản mục ảnh hưởng lớn tới kết quả vẫn cần được đối chiếu với báo cáo và thuyết minh doanh nghiệp công bố.
Khi một khoản mục có ảnh hưởng lớn, người dùng có thể quay lại hướng dẫn tra cứu và tải báo cáo tài chính mới nhất để đối chiếu với tài liệu doanh nghiệp công bố.
Khi nào bộ phân loại thực sự hữu ích?
Nếu chỉ cần xem một báo cáo của một doanh nghiệp, bảng rộng vẫn tiện và một trang tài chính có thể đã đủ. Bộ phân loại trở nên hữu ích khi bài toán mở rộng theo ba chiều: nhiều nguồn, nhiều kỳ và nhiều loại doanh nghiệp.
Ở quy mô đó, rào cản không còn là tải được một con số mà là biết hai con số có thật sự được phép đặt cạnh nhau hay không. vnstock_data giải quyết một phần của rào cản bằng cách tách id khỏi tên hiển thị, giữ thứ tự và phân cấp, đồng thời nhận diện cấu trúc theo loại hình doanh nghiệp.
Giá trị ứng dụng vì thế không dừng ở một bảng dữ liệu dễ đọc hơn. Cùng một hệ thống phân loại có thể phục vụ chuỗi thời gian, so sánh ngành, kiểm tra quan hệ giữa ba báo cáo, bộ lọc, cảnh báo và hồ sơ dữ liệu cho trợ lý AI. Nó giúp điểm xuất phát của những công việc đó bớt mơ hồ, dễ mở rộng và dễ kiểm tra hơn.
vnstock_data giúp lập trình viên kết nối tới dữ liệu từ các nguồn công khai và làm việc với đầu ra báo cáo tài chính đã đi qua bộ phân loại chung.
Thành viên Sponsor từ Bronze được sử dụng vnstock_data, nhận các bản cập nhật thư viện, tài liệu kỹ thuật, hỗ trợ và những chia sẻ kiến thức chuyên sâu. Khoản tài trợ đóng góp trực tiếp cho việc nghiên cứu, duy trì công cụ và phát triển cộng đồng.
👉 Tham gia Sponsor để sử dụng vnstock_data và đồng hành cùng dự án.








Bình luận