Logo FinLensFinLens Docs

Dữ liệu quỹ đầu tư Việt Nam bằng Python

Dùng finlens lấy danh sách 188 quỹ đầu tư Việt Nam, giá trị tài sản ròng theo ngày, danh mục nắm giữ theo tháng và phép tra ngược mã cổ phiếu về pandas DataFrame.

Danh mục của nhà đầu tư tổ chức là thứ khó lấy nhất và cũng dễ dùng nhất khi đã có. client.funds trả lời bốn câu: có những quỹ nào, quỹ này lãi lỗ ra sao, quỹ này đang nắm gì, và — chiều ngược lại — mã này đang được quỹ nào nắm.

188 quỹ; 149 quỹ có chuỗi giá trị tài sản ròng, 141 quỹ có danh mục nắm giữ. NAV theo ngày, danh mục theo tháng.

GọiTrả lời câu hỏi
client.funds.list()Thị trường có những quỹ nào, ai quản lý, quy mô bao nhiêu?
client.funds.nav()Giá trị tài sản ròng của quỹ này biến động thế nào?
client.funds.holdings()Quỹ này đang nắm những mã nào, tỷ trọng bao nhiêu?
client.funds.holders()Mã này đang được quỹ nào nắm?
client.funds.managers()Ai điều hành quỹ này?
import finlens

client = finlens.client()

ds = client.funds.list(fund_type="Quỹ ETF")
nav = client.funds.nav("E1VFVN30", start="2026-01-01")
ai = client.funds.holders("HPG")

Ba thứ sẽ làm bạn đọc sai số nếu bỏ qua

  1. Tổng weight_ratio của một quỹ KHÔNG bằng 1. Bảng chỉ có phần danh mục được công bố. Đừng tính tiền mặt bằng 1 − Σ.
  2. Mã quỹ không phải mã chứng khoán. Nó giữ nguyên hoa thường và chứa được dấu cách lẫn dấu +. 53/188 mã có ký tự ngoài [A-Z0-9].
  3. Tiền là VND thô, không phải nghìn VND như cột giá cổ phiếu.

Tổng tỷ trọng không bằng 1, và đó không phải lỗi

Đây là chỗ dễ mắc nhất của cả trang, nên nó đứng trước phần tham chiếu.

Bảng holdings() liệt kê phần danh mục được công bố, không phải toàn bộ tài sản. Đo trên mười hai quỹ:

for ma in ["ABBF", "ACBC-BGF", "AEIF", "ASBF", "ABEF"]:
    h = client.funds.holdings(ma)
    print(ma, len(h), round(h["weight_ratio"].sum(), 4))

# ABBF      14  0.8939
# ACBC-BGF  24  0.5691
# AEIF      23  0.4969
# ASBF       3  0.2757
# ABEF      28  0.9864

Dải đo được là 0,2426 đến 0,9864, và không quỹ nào vượt 0,99. Quỹ công bố ít dòng thì tổng càng thấp — ASBF có ba dòng và tổng 0,2757.

Nên 1 − Σ weight_ratio không phải tỷ lệ tiền mặt. Muốn biết quy mô thật, đọc total_nav của nav().

list() — danh mục quỹ và hồ sơ

client.funds.list(
    *,
    fund_type=None,
    structure=None,
    manager=None,
    refresh=False,
    on_error="warn",
) -> DataFrame
Tham sốKiểuMặc địnhNội dung
fund_typeFundTypeArgNone"Quỹ mở" (113) · "Quỹ ETF" (32) · "Quỹ thành viên" (31) · "Quỹ đóng" (12).
structureFundStructureArgNone"Quỹ cổ phiếu" (126) · "Quỹ trái phiếu" (32) · "Quỹ cân bằng" (29) · "Quỹ BĐS" (1).
managerstr | NoneNoneTên công ty quản lý. Miền mở — lấy từ cột management_org.
refreshboolFalseBỏ qua bản trong cache và lấy lại từ máy chủ.
on_errorOnError"warn"warn · raise · ignore.

Hai tham số đầu là từ vựng đóng và giữ nguyên tiếng Việt có dấu — chúng là nhãn phân loại của nguồn, không phải token ASCII.

Cột trả về

Frame có 28 cột. Những cột hay dùng nhất:

CộtdtypeNội dung
fund_codestringMã quỹ. Khoá bạn truyền lại cho bốn phương thức còn lại.
organization_idInt64Định danh tổ chức. Dùng khi mã quỹ nhập nhằng.
name · short_namestringTên đầy đủ và tên rút gọn.
fund_type · fund_structurestringLoại quỹ và cấu trúc tài sản.
management_org · monitoring_orgstringCông ty quản lý và ngân hàng giám sát.
schedulestringNhịp công bố NAV: Ngày (127) · Tuần (19) · Quý (5) · Tháng (4).
total_navFloat64Tổng giá trị tài sản ròng, VND thô.
nav_per_share_adjustedFloat64NAV trên một chứng chỉ quỹ, VND thô.
foreign_ratioFloat64Tỷ lệ sở hữu nước ngoài, thang 0–1.
nav_point_count · holding_period_countInt64Số điểm dữ liệu sẵn có. Bằng 0 nghĩa là nguồn chưa công bố.
min_investFloat64Số tiền đầu tư tối thiểu, VND thô.

Hai cột đếm cuối là cách rẻ nhất để biết trước một quỹ có dữ liệu hay không, thay vì gọi rồi nhận bảng rỗng.

client.funds.nav(
    funds,
    *,
    start=None,
    end=None,
    refresh=False,
    on_error="warn",
) -> DataFrame
Tham sốKiểuMặc địnhNội dung
fundsFundsArgMột mã, chuỗi nhiều mã ngăn bằng dấu phẩy, hoặc danh sách. Nhận cả organization_id.
start · endDateArgNone"2026-01-01". None là toàn bộ lịch sử.
refreshboolFalseBỏ qua bản trong cache và lấy lại từ máy chủ.
on_errorOnError"warn"warn · raise · ignore.

Đây là phương thức duy nhất của nhóm tự phân trang: bảng nguồn có hơn 136 nghìn dòng, vượt xa hạn mức một request, và thư viện đi hết chuỗi cursor giúp bạn. Nó cũng là phương thức duy nhất giữ cache 30 phút thay vì 24 giờ.

Cột trả về

CộtdtypeCó thể nullNội dung
fund_code · organization_idstring · Int64khôngĐịnh danh quỹ.
datedatetime64[ns]khôngNgày quan sát, không mang múi giờ.
nav_per_shareFloat64khôngNAV trên một chứng chỉ quỹ, VND thô.
nav_per_share_adjustedFloat64khôngBản đã điều chỉnh.
nav_change_ratioFloat64khôngBiến động so với kỳ trước, thang 0–1.
total_navFloat64khôngTổng NAV của quỹ, VND thô.
share_outstandingFloat64khôngSố chứng chỉ quỹ đang lưu hành.
fund_flowFloat64khôngDòng tiền vào ra.
foreign_volume · foreign_ratioFloat64khôngKhối ngoại: khối lượng và tỷ lệ thang 0–1.
close_price_adjustedFloat64Thị giá chứng chỉ quỹ. null với quỹ không niêm yết.
discountFloat64Chênh lệch thị giá so với NAV, VND thô.
discount_ratioFloat64Cùng đại lượng, thang 0–1.

Ba cột cuối là `null`, không phải `0`

Chỉ một phần quỹ có thị giá — phần còn lại không niêm yết nên không có giá nào để so với NAV. Nguồn ghi số 0 ở những dòng đó, nhưng một mức giá bằng 0 là bất khả, nên thư viện đổi chúng thành null.

Nếu giữ nguyên số 0, df["discount_ratio"].mean() sẽ lặng lẽ cộng hàng chục nghìn số 0 của những quỹ chưa từng giao dịch và kéo trung bình về gần 0.

holdings() — quỹ này đang nắm gì

client.funds.holdings(
    fund,
    *,
    date=None,
    refresh=False,
    on_error="warn",
) -> DataFrame
Tham sốKiểuMặc địnhNội dung
fundFundArgĐúng một quỹ. Mã quỹ hoặc organization_id.
dateDateArgNoneKỳ quan sát. None là kỳ gần nhất.
refreshboolFalseBỏ qua bản trong cache và lấy lại từ máy chủ.
on_errorOnError"warn"warn · raise · ignore.

Cột trả về

CộtdtypeNội dung
fund_code · organization_idstring · Int64Quỹ đang nắm.
datedatetime64[ns]Kỳ quan sát. Nhịp tháng.
asset_codestringMã tài sản được nắm.
asset_namestringTên tài sản.
asset_typestring"stock" hoặc "bond".
weight_ratioFloat64Tỷ trọng trong danh mục, thang 0–1.
quantityFloat64Số lượng nắm giữ.

asset_type cho biết mã trong asset_code tra ở đâu: "stock" thì nó là mã chứng khoán tra được bằng client.meta.symbols(), còn "bond" thì nó là mã trái phiếu tra bằng client.bonds.list().

holders() — mã này đang được quỹ nào nắm

client.funds.holders(
    symbols,
    *,
    date=None,
    refresh=False,
    on_error="warn",
) -> DataFrame
Tham sốKiểuMặc địnhNội dung
symbolsSymbolsArgMột hoặc nhiều mã chứng khoán — không phải mã quỹ.
dateDateArgNoneKỳ quan sát. None là mọi kỳ sẵn có.
refreshboolFalseBỏ qua bản trong cache và lấy lại từ máy chủ.
on_errorOnError"warn"warn · raise · ignore.

Đây là phép tra ngược của holdings(), và là thứ không tự tính được từ một bảng phẳng: nó đòi quét danh mục của mọi quỹ rồi gom theo mã.

⚠️ Kết quả trải nhiều kỳ, không phải một ảnh chụp. holders("HPG") trả về 80 dòng nằm trên 5 kỳ quan sát khác nhau — lọc theo date nếu bạn chỉ cần kỳ gần nhất:

k = client.funds.holders("HPG")
moi_nhat = k[k["date"] == k["date"].max()]

Cột trả về

CộtdtypeNội dung
symbolstringMã chứng khoán bạn hỏi.
fund_code · organization_idstring · Int64Quỹ đang nắm mã đó.
fund_short_namestringTên rút gọn của quỹ.
datedatetime64[ns]Kỳ quan sát.
weight_ratioFloat64Tỷ trọng của mã này trong danh mục của quỹ đó, thang 0–1.
quantityFloat64Số lượng quỹ đang nắm.

weight_ratio ở đây là tỷ trọng trong danh mục của quỹ, không phải tỷ lệ sở hữu trên vốn của doanh nghiệp. Hai đại lượng đó khác mẫu số hoàn toàn.

managers() — người điều hành quỹ

client.funds.managers(
    fund,
    *,
    refresh=False,
    on_error="warn",
) -> DataFrame
Tham sốKiểuMặc địnhNội dung
fundFundArgĐúng một quỹ. Mã quỹ hoặc organization_id.
refreshboolFalseBỏ qua bản trong cache và lấy lại từ máy chủ.
on_errorOnError"warn"warn · raise · ignore.

Cột trả về

CộtdtypeNội dung
fund_code · organization_idstring · Int64Quỹ.
full_namestringHọ tên người điều hành.
position_namestringChức danh.

Mã quỹ nhập nhằng

Một mã quỹ có thể thuộc hai tổ chức khác nhau. Khi đó thư viện không đoán:

import finlens

try:
    client.funds.nav("VVDIF")
except finlens.AmbiguousFundError as e:
    print(e.organization_ids)
    df = client.funds.nav(e.organization_ids[0])

Ngoại lệ mang theo fund_codeorganization_ids để bạn chọn. Mọi frame của nhóm này luôn trả về cả fund_code lẫn organization_id, nên bạn có sẵn định danh không nhập nhằng ngay trong kết quả.

Độ phủ có lỗ, và API không lọc chúng đi

Đo đượcCon số
Quỹ có chuỗi NAV149 / 188
Quỹ có danh mục nắm giữ141 / 188

Quỹ không có dữ liệu trả về bảng rỗng đủ cột đúng dtype, không phải lỗi:

df = client.funds.nav("A+ Fund")
len(df)          # 0
len(df.columns)  # 14
df["nav_per_share"].sum()   # 0.0 — không KeyError

Kiểm trước bằng nav_point_countholding_period_count của list() sẽ rẻ hơn là gọi rồi nhận bảng rỗng.

Thư viện không tính sẵn chỉ số nào

Không có suất sinh lợi 1 tháng / 3 tháng / từ đầu năm, không xếp hạng, không nhãn gom hay xả. finlens trả DataFrame; những thứ đó là hai dòng pandas:

nav = client.funds.nav("E1VFVN30", start="2026-01-01")
nav = nav.set_index("date")
loi_nhuan = nav["nav_per_share_adjusted"].pct_change(periods=20)

Tính ở máy chủ buộc phải chốt quy ước qui năm và quy ước điều chỉnh vào hợp đồng — loại quyết định sai một lần rồi sai lặng lẽ mãi. Sản phẩm MCP tính sẵn những chỉ số này, và đó là khác biệt có chủ đích giữa hai bề mặt.

Câu hỏi thường gặp

Làm sao biết một mã cổ phiếu đang được những quỹ đầu tư nào nắm giữ?

Gọi `client.funds.holders("HPG")` của thư viện Python finlens. Phương thức này làm phép tra ngược trên bảng danh mục nắm giữ: đầu vào là mã cổ phiếu, đầu ra là danh sách các quỹ đang nắm mã đó kèm tỷ trọng trong danh mục của từng quỹ và số lượng nắm giữ. Kết quả trải nhiều kỳ quan sát chứ không phải một ảnh chụp, nên hãy lọc theo cột `date` nếu bạn chỉ cần kỳ gần nhất. Đây là phép tra không tự tính được từ một bảng phẳng, vì nó đòi quét toàn bộ danh mục của mọi quỹ rồi gom theo mã.

Vì sao tổng tỷ trọng danh mục của một quỹ không bằng 100%?

Vì bảng danh mục nắm giữ chỉ liệt kê phần được công bố, không phải toàn bộ tài sản của quỹ. Đo trên mười hai quỹ bằng thư viện Python finlens, tổng cột `weight_ratio` chạy từ 0,24 đến 0,99 và không quỹ nào chạm 1,0. Hệ quả thực dụng: đừng tính tỷ lệ tiền mặt bằng công thức một trừ tổng tỷ trọng, con số đó sẽ sai và sai theo hướng phóng đại. Nếu bạn cần biết quy mô thật của quỹ, dùng cột `total_nav` của `client.funds.nav()` thay vì suy từ danh mục.

Mã quỹ khác mã cổ phiếu ở chỗ nào khi truyền vào hàm?

Mã quỹ không được viết hoa tự động và không phải lúc nào cũng là chuỗi chữ số liền nhau. Trong 188 quỹ mà thư viện Python finlens trả về, có 53 mã chứa ký tự ngoài bảng chữ hoa và chữ số — ví dụ `A+ Fund`, `ACBC-AGF`, `CSOP FTSE VN`. Vì thế hãy gõ đúng như cột `fund_code` của `client.funds.list()` trả về. Ngoài ra một vài mã trùng nhau giữa hai tổ chức khác nhau; khi đó thư viện ném `AmbiguousFundError` kèm danh sách `organization_ids` để bạn chọn đúng quỹ.

Giá trị tài sản ròng trả về đơn vị gì, có phải nghìn đồng như giá cổ phiếu không?

Không. Các cột tiền của nhóm quỹ trong thư viện Python finlens là **đồng Việt Nam thô**, khác với cột giá cổ phiếu vốn tính bằng nghìn đồng. Cụ thể `nav_per_share`, `nav_per_share_adjusted`, `total_nav`, `close_price_adjusted`, `discount` và `min_invest` đều là VND nguyên. Giá trị tài sản ròng trên một chứng chỉ quỹ trung bình vào khoảng vài trăm nghìn đồng, nên nếu bạn thấy con số cỡ trăm nghìn thì đó là đúng chứ không phải lỗi nhân sai thang đo.

Bốn cột có đuôi ratio nằm trong thang phần trăm hay thang 0–1?

Thang 0–1. Bốn cột `weight_ratio`, `nav_change_ratio`, `foreign_ratio` và `discount_ratio` của thư viện Python finlens đều là phân số, nên giá trị 0,0732 nghĩa là 7,32 phần trăm chứ không phải 0,07 phần trăm. Quy ước này áp cho toàn thư viện: hậu tố `_ratio` luôn là thang 0–1 còn hậu tố `_pct` mới là thang 0–100. Nguồn dữ liệu gốc đặt tên những cột này là `percent_*` trong khi giá trị thực là tỷ lệ, và tên ở đây được đổi để nói đúng thang đo.

Cập nhật lần cuối

Nội dung trang