Logo FinLensFinLens Docs

Thư viện Python lấy dữ liệu chứng khoán Việt Nam

Thư viện Python finlens trả dữ liệu chứng khoán Việt Nam về pandas DataFrame: giá cuối ngày, dữ liệu trong phiên, dòng tiền nhà đầu tư và báo cáo tài chính.

finlens là thư viện Python lấy dữ liệu thị trường chứng khoán Việt Nam — giá cuối ngày, giá trong phiên, từng lệnh khớp, dòng tiền theo nhóm nhà đầu tư, báo cáo tài chính và danh mục mã. Mọi truy vấn trả về pandas.DataFrame trần: ghi được to_parquet, nối được pd.concat, dùng được mọi tutorial pandas bạn từng đọc.

pip install finlens

Bắt đầu trong ba dòng

import finlens

client = finlens.client()        # đọc biến môi trường FINLENS_API_KEY
df = client.eod.stock.ohlcv("HPG,VCB,FPT", start="2026-01-01", end="2026-01-05")
  symbol       date   open   high    low  close      volume
0    FPT 2026-01-05  94.40  94.40  91.93  93.71   7129300.0
1    HPG 2026-01-05  23.57  23.70  22.94  23.17  59427911.0
2    VCB 2026-01-05    ...    ...    ...    ...         ...

Giá cổ phiếu tính bằng nghìn đồng — 23.17 nghĩa là 23.170 VND. Tên cột viết thường toàn bộ theo snake_case, kể cả date.

Chưa có khoá API thì bắt đầu ở Cài đặt và khoá API.

Cài đặt và khoá API

Yêu cầu môi trường, lấy khoá flk_..., truy vấn đầu tiên và cách kiểm tra đã cài đúng bản.

Dữ liệu cuối ngày (EOD)

ohlcv() cho cổ phiếu, chỉ số, phái sinh, chứng quyền và chỉ số ngành ICB; dòng tiền theo nhóm nhà đầu tư.

Dữ liệu trong phiên

Thanh giá theo phút, từng lệnh khớp với ticks(), và giá trị mua bán chủ động.

Báo cáo tài chính

Cân đối kế toán, kết quả kinh doanh và lưu chuyển tiền tệ theo năm hoặc theo quý, ở dạng long.

Quỹ đầu tư

188 quỹ, 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ã này đang được quỹ nào nắm.

Trái phiếu doanh nghiệp

6.820 lô trái phiếu của 940 tổ chức phát hành, kèm dư nợ còn lại và lịch đáo hạn.

Danh mục theo dõi

Bề mặt duy nhất có GHI dữ liệu, và nó dùng chung với web.

Tra cứu danh mục

Có những mã nào, ngành ICB nào, chứng quyền nào — ba lời gọi mở đầu mọi phiên làm việc.

Dữ liệu vĩ mô

CPI, tỷ giá, lãi suất liên ngân hàng, nghiệp vụ thị trường mở của NHNN và cán cân thương mại.

Chỉ báo kỹ thuật

RSI, MACD, Bollinger và 61 mẫu nến ngay trên DataFrame giá — tự tách theo mã, không phải tự gọi TA-Lib.

Kiểu dữ liệu

Giá trị hợp lệ của từng tham số, hình dạng df.attrs["finlens"], và các TypedDict của whoami() / limits().

Xử lý lỗi

Cây ngoại lệ gốc FinLensError, mã FL_*, và các lớp cảnh báo tắt được chọn lọc.

Ví dụ thực chiến

17 Jupyter notebook chạy trên dữ liệu thật: dashboard thị trường, screener định giá, screener tín hiệu kỹ thuật và backtest.

Lấy được những dữ liệu gì

Một đối tượng client duy nhất, chia thành các namespace theo loại dữ liệu và loại tài sản.

Đường dẫnDữ liệuVí dụ
client.eod.stockGiá cuối ngày của cổ phiếu, ETF và chứng chỉ quỹclient.eod.stock.ohlcv("HPG")
client.eod.indexGiá cuối ngày của chỉ sốclient.eod.index.ohlcv("VNINDEX")
client.eod.derivativeGiá cuối ngày của hợp đồng phái sinhclient.eod.derivative.ohlcv("VN30F1M")
client.eod.warrantGiá cuối ngày của chứng quyền có bảo đảmclient.eod.warrant.ohlcv("CHPG2628")
client.eod.sectorChỉ số ngành ICBclient.eod.sector.ohlcv("8355")
client.eod.stock.investorDòng tiền theo nhóm nhà đầu tư, cấp mãclient.eod.stock.investor.flow("HPG")
client.eod.sector.investorDòng tiền theo nhóm nhà đầu tư, cấp ngànhclient.eod.sector.investor.flow("8355")
client.eod.index.investorDòng tiền theo nhóm nhà đầu tư, cấp chỉ sốclient.eod.index.investor.flow("VNINDEX")
client.intraday.stockTrong phiên: thanh giá, tick, mua bán chủ độngclient.intraday.stock.ticks("HPG", date="2026-08-03")
client.intraday.indexTrong phiên, chỉ sốclient.intraday.index.ohlcv("VNINDEX", interval="5min")
client.intraday.derivativeTrong phiên, phái sinhclient.intraday.derivative.net_active_value("VN30F1M")
client.financialsBáo cáo tài chínhclient.financials.statement("HPG")
client.metaDanh mục mã, cây ngành, chứng quyềnclient.meta.symbols(exchange="HOSE")
client.financials (thuyết minh)Thuyết minh BCTC, phân nhóm nợ ngân hàngclient.financials.notes("VCB", section="loan_quality")
client.fundsQuỹ đầu tư: NAV, danh mục, tra ngượcclient.funds.holders("HPG")
client.bondsTrái phiếu doanh nghiệpclient.bonds.list(outstanding=True)
client.macroVĩ mô: chỉ tiêu, OMO, xuất nhập khẩuclient.macro.series("gia_vang_giao_ngay")
client.watchlistDanh mục theo dõi — đọc VÀ ghi, dùng chung webclient.watchlist.symbols(42)

Ngoài dữ liệu, client còn trả lời về chính nó: client.whoami() cho gói dịch vụ và tình trạng khoá, client.limits() cho hạn mức hiệu lực, client.status() cho trạng thái service, client.cache.stats() cho số liệu cache.

Giá cuối ngày

client.eod.stock.ohlcv("HPG", start="2020-01-01", interval="1w")
client.eod.stock.ohlcv("HPG", adjusted=False)     # giá khớp lệnh thật của phiên
client.eod.index.ohlcv("VNINDEX", interval="1mo")
client.eod.sector.ohlcv("8355")                   # chỉ số ngành, mã ICB cấp 4

Cổ phiếu có dữ liệu từ 2007, chỉ số ngành từ 2000. Giá cổ phiếu mặc định đã điều chỉnh quyền; adjusted=False cho chuỗi giá đúng như phiên hôm đó, dùng khi đối chiếu sao kê hoặc dựng lại một lệnh cũ. Cột volume không đổi theo cờ này — nó luôn là số cổ phiếu thật.

Gộp nhóm chạy ở server: interval="1w" trả về thanh tuần đã gộp, không phải thanh ngày để bạn tự gộp lại. Một kỳ đã gộp được dán nhãn bằng phiên giao dịch cuối trong kỳ, nên interval="1mo" cho tháng 1/2025 ra 2025-01-24 — phiên cuối trước kỳ nghỉ Tết — chứ không phải 2025-01-01 hay 2025-01-31.

Trong phiên và tick-by-tick

client.intraday.stock.ohlcv("HPG", interval="5min")
client.intraday.stock.ticks("HPG", date="2026-08-03")         # từng lệnh khớp
client.intraday.stock.net_active_value("HPG", interval="1h")  # mua/bán chủ động

Tick có từ 2022. Cột thời gian ở đây tên time và mang múi giờ +07:00, khác cột date naive của dữ liệu cuối ngày — một thanh trong phiên là một thời điểm, không phải một ngày giao dịch.

ticks() lấy một mã, một phiên mỗi lời gọi, và giới hạn đó nằm trong chính chữ ký hàm: tham số đầu là symbol số ít. Một phiên phái sinh sôi động là hàng chục nghìn dòng.

Giá trong phiên là giá thô

Ngược với client.eod.stock, giá trong phiên chưa điều chỉnh quyền. Nối một chuỗi trong phiên vào một chuỗi cuối ngày mà không đọc df.attrs["finlens"]["price_basis"] sẽ cho một bậc thang giả ở đúng ngày chia tách, và không có lỗi nào báo.

client.intraday.index không có net_active_value(), và đó không phải một thiếu sót: bảng tick của chỉ số không ghi chiều lệnh chủ động, nên không có dữ liệu nào để tính. Một sự vắng mặt trong dữ liệu là một sự vắng mặt trong API.

Dòng tiền theo nhóm nhà đầu tư

client.eod.stock.investor.flow("HPG", group="foreign")
client.eod.stock.investor.breakdown("HPG")        # nhiều nhóm trong một frame
client.eod.sector.investor.flow("8355")           # theo ngành ICB

flow() hỏi một nhóm, breakdown() hỏi nhiều nhóm — cùng một dataset, cùng một bộ cột. Cột group luôn có mặt, kể cả khi bạn chỉ hỏi một nhóm, vì đó là chỗ duy nhất nói được "con số này tính trên cơ sở nào" ở mức từng dòng.

NhómCó từPhạm vi
foreign2010cả ba sàn
foreign_individual, foreign_institutional2024HOSE
local_individual, local_institutional2024HOSE
proprietary (tự doanh)2022cả ba sàn

Sáu nhóm không cùng một vũ trụ dữ liệu

Bốn nhóm chi tiết (cá nhân/tổ chức × trong nước/nước ngoài) là một phân hoạch đầy đủ của khớp lệnh: tổng mua ròng của chúng bằng 0, vì mua ròng của nhóm này là bán ròng của nhóm kia.

foreign gồm cả giao dịch thoả thuận nên chồng lấn lên hai nhóm ngoại chi tiết, còn proprietary (tự doanh) đến từ một nguồn khác hẳn và không phải nhóm con của local_institutional. Cộng cả sáu nhóm lại cho ra một con số vô nghĩa.

Ở cấp ngành chỉ có foreign và proprietary; bốn nhóm chi tiết không tồn tại ở đó. Ở cấp chỉ số, hiện chỉ VNINDEX có dữ liệu.

Báo cáo tài chính

client.financials.statement("HPG", kind="balance_sheet", period="quarterly")
client.financials.periods("HPG")                        # kỳ nào có số liệu
client.financials.line_items(com_type="NH", kind="balance_sheet")

Có từ 2004. Frame ở dạng long — một dòng là một khoản mục của một kỳ — và mỗi dòng mang company_type của chính nó, nên statement(["HPG", "VCB"]) chạy được dù HPG là doanh nghiệp phi tài chính còn VCB là ngân hàng. Bốn loại hình (CT phi tài chính, NH ngân hàng, CK chứng khoán, BH bảo hiểm) có bốn cây khoản mục khác nhau.

Tra cứu danh mục

client.meta.symbols()                          # toàn bộ danh mục
client.meta.symbols(exchange="HOSE")
client.meta.symbols(icb="8300")                # toàn ngành ngân hàng
client.meta.symbols(kind="fund")               # chứng chỉ quỹ niêm yết
client.meta.sectors(level=2)                   # cây ngành ICB
client.meta.warrants(underlying="HPG")

Đây thường là lời gọi đầu tiên của một phiên làm việc: trước khi hỏi giá của một mã, người ta hỏi có những mã nào. Tham số icb= nhận cả mã cấp 2 lẫn mã cấp 4, nên bạn không cần biết mã mình đang cầm thuộc cấp nào.

tickers = client.meta.symbols(exchange="HOSE")["symbol"].tolist()

Chi tiết ba phương thức, bộ lọc và cột trả về ở Tra cứu danh mục.

Chỉ báo kỹ thuật

df = client.eod.stock.ohlcv(["HPG", "VCB"], start="2024-01-01")

df = df.finlens.rsi(14).finlens.macd()     # thêm cột vào bản sao của frame
df.finlens.patterns("doji")                # quét mẫu nến, dạng dài

135 hàm TA-Lib đi kèm gói, không phải một namespace của client mà là một accessor trên chính DataFrame — nên nó tự tách theo cột symbol và bạn không bao giờ để cửa sổ của mã này ăn vào giá của mã kia. Chi tiết ở Chỉ báo kỹ thuật.

Đơn vị — đọc trước khi tính toán

Đây là nguồn lỗi số một khi làm việc với dữ liệu chứng khoán Việt Nam, và nó sai âm thầm: không exception nào, chỉ là một con số sai.

Loại dữ liệuCột giáKhối lượngGiá trị tiền
Cổ phiếu, ETF, chứng chỉ quỹnghìn VND (22.3 = 22.300 đ)cổ phiếuVND
Chỉ sốđiểm chỉ sốcổ phiếu—
Chỉ số ngành ICBđiểm chỉ sốcổ phiếu—
Phái sinhđiểm chỉ sốhợp đồngVND
Chứng quyềnVND thôchứng quyềnVND
Dòng tiền nhà đầu tư—cổ phiếuVND thô
Báo cáo tài chính——VND thô

Đơn vị được khai báo chứ không phải đoán — mỗi frame mang theo đơn vị của chính nó:

df.attrs["finlens"]["units"]          # {'close': 'kVND', 'volume': 'share', ...}
df.attrs["finlens"]["price_basis"]    # 'adjusted' hoặc 'raw'
df.attrs["finlens"]["as_of"]          # mốc nước của dữ liệu

`df.attrs` không sống sót qua `merge` và `concat`

attrs mang tính thông tin, không phải cơ chế bảo vệ — pandas bỏ nó khi ghép frame. Đọc đơn vị trước khi ghép.

Bảo vệ thật nằm ở chỗ tách namespace: client.eod.stock và client.eod.index không bao giờ trả về cùng một bảng, nên một lời gọi không thể trộn nghìn VND với điểm chỉ số.

Từ vựng interval

NhómGiá trị hợp lệ
Cuối ngày1d · 1w · 1mo · 3mo · 6mo · 1y
Trong phiên1min · 5min · 15min · 30min · 1h · 4h

Không có `1m` và không có `1M`

Trong từ vựng cũ 1M là một tháng còn 1m là một phút — phân biệt duy nhất bằng chữ hoa, và hai giá trị đó lệch nhau 43.200 lần. Ở đây tháng là 1mo, phút là 1min, và "1M" bị từ chối kèm gợi ý thay vì âm thầm trả về một biểu đồ sai.

Hai nhóm dán nhãn ngược nhau, và chỗ này dễ suy sai từ nhóm này sang nhóm kia:

NhómCột thời gianMốc của một kỳ đã gộp
Cuối ngàydate, naivePhiên giao dịch cuối trong kỳ
Trong phiêntime, mang +07:00Đầu bước thời gian

Cuối ngày lấy phiên cuối vì close của dòng ấy là giá của đúng phiên đó — nhãn theo lịch sẽ trỏ tới một ngày mà thị trường không mở, và với kỳ đang dở thì trỏ cả tới một ngày ở tương lai. Chi tiết ở Giá cuối ngày.

Một mã lỗi không làm mất các mã còn lại

Mặc định on_error="warn": bạn nhận về những mã lấy được kèm một PartialDataWarning, và chi tiết từng mã hỏng nằm ở df.attrs["finlens"]["failed"]. Đặt on_error="raise" nếu muốn một mã hỏng làm cả lời gọi thất bại — ngoại lệ PartialFetchError vẫn mang theo phần đã lấy được ở .data.

Mọi phương thức cũng nhận refresh=True để bỏ qua cache và lấy lại từ server.

Kết quả rỗng vẫn đúng cột, đúng kiểu: ngày thị trường nghỉ, df["close"] vẫn chạy và không KeyError. Bạn không cần bọc if not df.empty quanh mọi lời gọi. Chi tiết ở Xử lý lỗi.

Yêu cầu môi trường

  • Python 3.11+
  • pandas 3.0+
  • Chạy được trên Windows, Linux và macOS
  • Có gợi ý kiểu theo chuẩn PEP 561, nên autocomplete và mypy hoạt động đầy đủ

Giấy phép MIT. Gói phát hành trên PyPI.

Bản bất đồng bộ

Mọi thứ có bản async với cùng chữ ký, dùng chung một lõi với bản đồng bộ — không có chuyện một bên được sửa bug còn bên kia thì không.

import asyncio
import finlens

async def main():
    async with finlens.AsyncClient() as client:
        df = await client.eod.stock.ohlcv("HPG,VCB")

asyncio.run(main())

Viết nhanh hơn với extension VS Code

Nếu bạn code trong VS Code, extension FinLens là môi trường tự nhiên cho thư viện này. Nhấn Ctrl+Alt+F để chèn thẳng lời gọi client.eod... đúng như trên trang này — extension tự thêm import finlens và client = finlens.client(), tự điền mã (tìm bằng ticker hoặc tên doanh nghiệp), và không ghi khoá API vào code.

  • Cây Dữ liệu duyệt toàn bộ bề mặt API kèm đơn vị từng cột; bấm một dataset là xem trước DataFrame ngay trong editor mà chưa cần chạy file.
  • 135 hàm TA-Lib — đúng bộ accessor df.finlens ở trên — nằm trong cây, chèn kèm tham số mặc định.
  • Ngoài viết code còn có bảng giá realtime, biểu đồ kỹ thuật và Jobs lên lịch chạy file Python cục bộ.

Không viết code thì dùng gì?

Nếu bạn muốn hỏi thẳng bằng tiếng Việt thay vì viết truy vấn, hãy dùng FinLens MCP — máy chủ Model Context Protocol nối Claude, ChatGPT và Gemini vào cùng nguồn dữ liệu chứng khoán Việt Nam, có thêm các nhóm dữ liệu SDK chưa phủ như báo cáo phân tích của công ty chứng khoán, quỹ đầu tư và trái phiếu doanh nghiệp. Muốn xem dạng giao diện đồ thị thì vào FinLens Web.

Hỗ trợ

Cần tư vấn cách dùng thì liên hệ trực tiếp:

Gặp lỗi của thư viện hoặc của một notebook ví dụ thì mở issue trên GitHub — nơi lỗi được ghi lại, tìm lại được, và người khác gặp cùng vấn đề cũng thấy.

Báo lỗi trên GitHub

Kèm theo finlens.build_info() và request_id trong thông báo lỗi — hai thứ đó cho biết chính xác bản build nào và request nào, giúp tái hiện nhanh.

Trước khi báo, xem nhật ký thay đổi — có thể lỗi đã được sửa ở bản mới hơn.

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

Làm sao lấy dữ liệu giá cổ phiếu Việt Nam bằng Python?

Cài thư viện bằng `pip install finlens`, tạo client bằng `finlens.client()` (khoá đọc từ biến môi trường `FINLENS_API_KEY`), rồi gọi `client.eod.stock.ohlcv("HPG", start="2026-01-01", end="2026-01-31")`. Hàm trả về `pandas.DataFrame` với bảy cột viết thường `symbol`, `date`, `open`, `high`, `low`, `close`, `volume`, dùng được ngay trong notebook hay pipeline dữ liệu. Giá cổ phiếu tính bằng nghìn đồng: `23.17` nghĩa là 23.170 VND. Mặc định `interval="1d"` cho một dòng mỗi phiên; với `"1w"` hay `"1mo"` thì mỗi dòng là một kỳ đã gộp và cột `date` mang phiên giao dịch cuối trong kỳ.

Thư viện finlens cung cấp những loại dữ liệu chứng khoán nào?

Đối tượng client của finlens chia theo namespace. Giá cuối ngày nằm ở `client.eod.stock`, `client.eod.index`, `client.eod.derivative`, `client.eod.warrant` và `client.eod.sector` — mỗi loại tài sản một namespace vì mỗi loại một đơn vị. Dòng tiền theo nhóm nhà đầu tư ở `client.eod.stock.investor.flow()` và `breakdown()`. Dữ liệu trong phiên ở `client.intraday.stock` với `ohlcv()`, `ticks()` và `net_active_value()`. Báo cáo tài chính ở `client.financials.statement()`, còn danh mục mã, cây ngành ICB và chứng quyền ở `client.meta.symbols()`, `client.meta.sectors()`, `client.meta.warrants()`.

Tham số group của finlens nhận giá trị nào, và cộng được nhóm nào lại?

Ở cấp mã, `group` nhận sáu giá trị: `"foreign"`, `"foreign_individual"`, `"foreign_institutional"`, `"local_individual"`, `"local_institutional"` và `"proprietary"` (tự doanh). Nhưng chỉ bốn nhóm chi tiết — cá nhân và tổ chức, trong nước và nước ngoài — là một phân hoạch đầy đủ, tổng `net_value` của chúng bằng 0. `foreign` gồm cả giao dịch thoả thuận nên chồng lấn lên hai nhóm ngoại chi tiết, còn `proprietary` đến từ nguồn khác hẳn và không phải nhóm con của `local_institutional`; cộng cả sáu lại cho ra một con số vô nghĩa. Ở cấp ngành chỉ có `"foreign"` và `"proprietary"`.

Thư viện finlens yêu cầu môi trường gì và dùng giấy phép nào?

finlens yêu cầu Python từ 3.11 trở lên và pandas từ 3.0 trở lên, chạy được trên Windows, Linux và macOS. Lệnh `pip install finlens` kéo theo bốn phụ thuộc: `pandas`, `httpx`, `packaging` và `ta-lib` — `pydantic` và `requests` không còn ở bản 1.x. Gói có gợi ý kiểu theo chuẩn PEP 561 nên autocomplete và `mypy` hoạt động đầy đủ. Thư viện phát hành trên PyPI dưới giấy phép MIT.

Đơn vị giá trong dữ liệu chứng khoán Việt Nam trả về là gì?

Với thư viện Python finlens, cổ phiếu và ETF trả giá bằng nghìn VND (`22.3` là 22.300 đồng), chỉ số và phái sinh trả bằng điểm chỉ số, còn chứng quyền trả bằng VND thô. Giá trị dòng tiền nhà đầu tư và số liệu báo cáo tài chính luôn là VND thô. Đừng đoán theo tên cột: mỗi DataFrame mang theo đơn vị của chính nó ở `df.attrs["finlens"]["units"]`, ví dụ `df.attrs["finlens"]["units"]["close"]` trả `'kVND'` với cổ phiếu và `'index_point'` với chỉ số. Lưu ý `df.attrs` không sống sót qua `pd.concat` hay `merge` của pandas.

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

Nội dung trang