Skip to main content

finlens — Dữ liệu chứng khoán Việt Nam cho Python

PyPI Python License

Thư viện Python lấy dữ liệu thị trường chứng khoán Việt Nam — giá cuối ngày và trong phiên, khớp lệnh từng lệnh (tick-by-tick), dòng tiền theo nhóm nhà đầu tư, báo cáo tài chính, chỉ số vĩ mô và danh mục mã — trả về thẳng dưới dạng pandas.DataFrame, kèm 135 hàm chỉ báo kỹ thuật TA-Lib chạy thẳng trên frame nhiều mã.

Phủ HOSE (HSX), HNX và UPCOM: 1.646 cổ phiếu và chứng chỉ quỹ, 341 chứng quyền có bảo đảm còn hạn (750 kể cả đã đáo hạn), hợp đồng phái sinh VN30F1M, và các chỉ số VNINDEX, VN30, HNX-INDEX, UPCOM-INDEX.

Python library for Vietnam stock market data — daily and intraday OHLCV, tick-by-tick trades, foreign investor flows, financial statements, plus 135 TA-Lib technical indicators. Covers HOSE, HNX and UPCOM. Returns pandas DataFrames.

pip install finlens
import finlens

client = finlens.client(api_key="flk_...")  # hoặc đặt biến FINLENS_API_KEY

df = client.eod.stock.ohlcv("HPG,VCB,FPT", start="2026-01-01", end="2026-01-05")
print(df)
#   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á ở đây tính bằng nghìn đồng — 23.17 nghĩa là 23.170 VND.


Mục lục


Vì sao dùng finlens

Trả về pandas.DataFrame trần, không phải wrapper. Ghi được to_parquet, nối được pd.concat, dùng được mọi tutorial pandas bạn từng đọc.

Nhiều mã trong một request. ohlcv("HPG,VCB,FPT,...") với 50 mã là một lời gọi HTTP chứ không phải 50 lời gọi tuần tự.

Đơn vị được khai báo, không phải đoán. Mỗi frame mang theo df.attrs["finlens"]["units"]. Giá cổ phiếu Việt Nam thường ghi bằng nghìn đồng còn chứng quyền bằng đồng — nhầm chỗ này sai đúng 1000 lần và không có lỗi nào báo. Thư viện tách chúng thành các namespace riêng nên một lời gọi không bao giờ trả về bảng trộn hai đơn vị.

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 — không KeyError.

Type hint đầy đủ, docstring tiếng Việt. Autocomplete và help() hoạt động; mypy --strict chạy sạch.


Cài đặt và khoá API

pip install finlens

Yêu cầu Python 3.11+ và pandas 3.0+. Từ bản 1.2.0, pip install kéo thêm ta-lib — nó là nền của phần chỉ báo kỹ thuật.

Lấy khoá API

Khoá có dạng flk_... và có hai đường tạo. Hướng dẫn đầy đủ ở docs.finlens.vn/python-sdk/cai-dat.

⚠️ Mỗi tài khoản chỉ có MỘT khoá. Khoá thuộc tài khoản, không thuộc thiết bị. Tạo khoá mới — ở extension hay trên web — thu hồi ngay khoá cũ, và mọi nơi đang dùng nó (máy khác, file cấu hình, tác vụ định kỳ) sẽ ngừng chạy.

Khoá mới chỉ hiển thị đúng một lần. Về sau máy chủ chỉ trả lại 12 ký tự đầu qua client.whoami()["key"]["prefix"].

Cách 1 — trong extension VS Code (khuyến nghị).

Hộp thoại Tạo API key mới trong extension FinLens của VS Code, cảnh báo khoá hiện tại sẽ bị thu hồi

Mở FinLens trên Activity Bar → Trang chủ → nút API key → Thu hồi và tạo mới.

Đường này được khuyến nghị vì khoá lưu thẳng vào VS Code SecretStorage — không nằm trong file, không nằm trong code, không hiện ở cell notebook nào.

Để finlens.client() ở notebook và terminal đọc được khoá đó tự động, chạy lệnh FinLens: Ghi API key ra file cấu hình (cho notebook và terminal ngoài). Extension ghi khoá vào finlens.toml, và thư viện đọc nó ở tầng thứ ba của thứ tự ưu tiên bên dưới — bạn không phải đặt biến môi trường bằng tay.

Cách 2 — trên finlens.vn.

Cửa sổ Cài đặt tài khoản trên finlens.vn, mục API key dùng cho thư viện Python finlens

Đăng nhập finlens.vn → Cài đặt tài khoản → mục API key → Tạo API key mới, rồi sao chép khoá.

Ba cách đưa khoá vào client

import finlens

# Cách 1 — truyền thẳng
client = finlens.client(api_key="flk_...")

# Cách 2 — biến môi trường FINLENS_API_KEY (khuyến nghị)
client = finlens.client()

# Dùng như context manager để đóng kết nối gọn gàng
with finlens.client() as client:
    df = client.eod.stock.ohlcv("HPG")

Cách 3 — file cấu hình. Tiện khi bạn dùng nhiều notebook và không muốn đặt biến môi trường ở mỗi nơi. Đây cũng là file mà extension VS Code ghi giúp bạn:

# ./finlens.toml, hoặc %APPDATA%\finlens\config.toml (Windows)
#                hoặc ~/.config/finlens/config.toml (macOS, Linux)
[default]
api_key = "flk_..."

⚠️ File đầu tiên tìm thấy là file duy nhất được đọc — không merge. Một ./finlens.toml trong thư mục dự án sẽ che hoàn toàn file cấu hình máy của bạn, kể cả những khoá nó không khai. Nếu thiếu khoá, thông báo lỗi sẽ nói rõ nó đã tìm ở đâu và file nào che file nào.

Thứ tự ưu tiên đầy đủ: api_key= → FINLENS_API_KEY → file cấu hình.

⚠️ Đừng viết khoá thẳng vào code rồi đẩy lên Git. Trong notebook dùng chung, finlens.client() không tham số là an toàn nhất: khoá không xuất hiện ở cell nào, nên nó cũng không nằm trong file .ipynb bạn gửi đi. Nếu dùng ./finlens.toml trong thư mục dự án, thêm nó vào .gitignore.

Tạo client không chạm mạng — an toàn để đặt ở cell đầu notebook hoặc trong __init__ của một lớp.

client.whoami()  # gói dịch vụ, hạn mức, ngày hết hạn
client.status()  # trạng thái service (không cần khoá)

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

Giá cuối ngày (EOD)

client.eod.stock.ohlcv("HPG", start="2020-01-01", interval="1w")
client.eod.index.ohlcv("VNINDEX")
client.eod.derivative.ohlcv("VN30F1M")
client.eod.warrant.ohlcv("CHPG2628")
client.eod.sector.ohlcv("8355")  # chỉ số ngành ICB

Cổ phiếu có từ 2007, chỉ số ngành từ 2000. interval nhận 1d, 1w, 1mo, 3mo, 6mo, 1y — gộp nhóm chạy ở server.

Giá cổ phiếu mặc định đã điều chỉnh quyền; thêm adjusted=False để lấy giá thô đúng như phiên hôm đó.

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. interval trong phiên: 1min, 5min, 15min, 30min, 1h, 4h.

Cột side của tick có ba giá trị: buy (bên mua nâng giá chạm bên bán), sell (bên bán hạ giá chạm bên mua), và auction (khớp lệnh định kỳ ATO/ATC). Phiên định kỳ không có bên chủ động nên xếp nó vào mua hay bán đều sai — nó chiếm 1,4%–8,6% khối lượng tuỳ mã, quá lớn để giấu.

ticks() lấy một mã, một phiên mỗi lần gọi: một phiên phái sinh sôi động là hơn 90.000 dòng.

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

client.eod.stock.investor.flow("HPG", group="foreign")
client.eod.stock.investor.breakdown("HPG")  # tất cả các nhóm
client.eod.sector.investor.flow("8355")  # theo ngành ICB
Nhóm Có từ Phạm vi
foreign 2010 cả ba sàn
foreign_individual, foreign_institutional 2024 HOSE
local_individual, local_institutional 2024 HOSE
proprietary (tự doanh) 2022 cả ba sàn

Bốn nhóm chi tiết cộng lại bằng 0 — mua ròng của nhóm này là bán ròng của nhóm kia. foreign và proprietary đến từ nguồn khác và nằm trên trục riêng; meta.additive_groups trong response nói rõ nhóm nào cộng được với nhau.

Ở hợp đồng phái sinh chỉ có foreign và proprietary; bốn nhóm chi tiết không tồn tại và kiểu của tham số đã chặn từ lúc gõ code:

client.eod.derivative.investor.flow("VN30F1M", group="proprietary")
client.eod.derivative.investor.breakdown("VN30F1M")  # cả hai nhóm

Sổ lệnh đặt và khối lượng chủ động

client.eod.stock.supply_demand("HPG")  # lệnh ĐẶT vào sổ
client.eod.stock.active_volume("HPG")  # khối lượng khớp chủ động
client.eod.derivative.active_volume("VN30F1M")

⚠️ Hai bảng này không trừ được cho nhau, và không trừ được cho ohlcv().

supply_demand() là lệnh đặt, không phải lệnh khớp: trung vị buy_order_volume / volume là 2,756 lần, và 99,77% số dòng có khối lượng đặt lớn hơn hoặc bằng khối lượng khớp. Đó là lý do mọi cột mang chữ _order_. Ba cột _count là Int64 nullable — server phát null ở 11,38% số dòng, nên kiểm bằng .isna() chứ đừng so với 0. Chỉ có ở client.eod.stock.

active_volume() là khối lượng, hai rổ, không có cột tiền nào. Nó khác hẳn client.intraday.*.net_active_value() vốn có ba rổ và tính bằng VND — cùng một phiên VN30F1M, hai đại lượng lệch nhau 57,5% · 520% · 30,1%.

Chênh lệch phái sinh và chỉ số cơ sở

client.eod.derivative.basis("VN30F1M")  # theo phiên
client.intraday.derivative.basis("VN30F1M")  # bước 1 phút
basis     = future_close - spot_close    # điểm chỉ số
basis_pct = basis / spot_close * 100     # phần trăm, thang 0-100

Mẫu số là giá chỉ số, không phải giá hợp đồng — hai mẫu số chỉ lệch nhau khoảng 0,3% nên chọn nhầm gần như không nhìn ra được. Không method nào có interval: gộp một chênh lệch qua nhiều bước không có nghĩa hiển nhiên nào.

Mã phái sinh không có chỉ số cơ sở đi vào phần lỗi theo từng mã (FL_DATA_NO_UNDERLYING) và không làm hỏng cả lời gọi.

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. Bốn loại hình doanh nghiệp (CT phi tài chính, NH ngân hàng, CK chứng khoán, BH bảo hiểm) có cây chỉ tiêu khác nhau, nhưng frame ở dạng long và mỗi dòng mang company_type của chính nó — nên statement(["HPG", "VCB"]) chạy được dù hai mã khác loại hình.

Tra cứu danh mục

client.meta.symbols()  # 1.645 mã
client.meta.symbols(exchange="HOSE")  # 431 mã
client.meta.symbols(icb="8300")  # toàn ngành ngân hàng
client.meta.symbols(kind="fund")  # 24 chứng chỉ quỹ niêm yết
client.meta.sectors(level=2)  # 19 ngành ICB cấp 2
client.meta.sectors(level=4)  # 106 ngành ICB cấp 4
client.meta.warrants(underlying="HPG")  # chứng quyền của HPG

Tham số icb= nhận cả mã cấp 2 lẫn cấp 4 — bạn không cần biết mã mình cầm thuộc cấp nào.

# Lấy danh sách mã để lặp
tickers = client.meta.symbols(exchange="HOSE")["symbol"].tolist()

Vĩ mô

Chỉ số thống kê, nghiệp vụ thị trường mở và xuất nhập khẩu — nguồn là Tổng cục Thống kê và Ngân hàng Nhà nước. 3.348 chuỗi chỉ tiêu, cập nhật hằng ngày.

# Tra mã trước, rồi mới hỏi số — mã không đoán được từ tên
ds = client.macro.indicators(topic="cpi", freq="monthly")
df = client.macro.series(ds["code"].tolist()[:5])

client.macro.series("ty_gia_trung_tam_daily")  # tỷ giá trung tâm, VND
client.macro.series("gia_vang_giao_ngay_daily")  # giá vàng, USD/Ounce
client.macro.omo(kind="net_pump")  # NHNN bơm hút ròng
client.macro.trade(flow="export", by="country")  # xuất khẩu theo đối tác

⚠️ Đây là namespace duy nhất mà unit là một CỘT chứ không phải thuộc tính của cả bảng. Hỏi hai chỉ tiêu bất kỳ là có thể nhận % nằm cạnh USD/thùng trong cùng cột value — mọi namespace khác không bao giờ trộn đơn vị vì mỗi loại tài sản nằm ở một namespace riêng. Đọc unit theo từng dòng.

⚠️ Cột date là cuối kỳ quan sát, không phải một phiên giao dịch; cột period đi kèm mới nói kỳ nào ("7-2026", "Q1-2026").

Trái phiếu doanh nghiệp

6.820 lô trái phiếu của 940 tổ chức phát hành: điều khoản từng lô, dư nợ đang lưu hành, và hồ sơ tổ chức đã tổng hợp sẵn.

client.bonds.list()  # trọn danh mục, một request
client.bonds.list(sector="real_estate", outstanding=True)  # còn lưu hành
client.bonds.list(symbol="HPG")  # đi bằng mã cổ phiếu
client.bonds.list(outstanding=True, maturity_to="2027-08-11")  # tường đáo hạn
client.bonds.issuers(has_stock=True)  # 128 tổ chức có niêm yết

⚠️ Đây là ẢNH CHỤP, không phải chuỗi thời gian. Nguồn crawl một lần mỗi ngày rồi ghi đè — không có lịch sử dư nợ. matured= so với mốc ở df.attrs["finlens"]["source_updated_at"], không so với đồng hồ của bạn.

⚠️ Tiền đi theo CẶP _mvnd / _usd, mỗi dòng đúng một vế khác null và currency quyết định vế nào. Hai vế không cộng được với nhau — nguồn không chứa tỷ giá nào, nên "tổng dư nợ TPDN" tính từ outstanding_value_mvnd bỏ sót phần USD, xấp xỉ 4,6% thị trường. Thang đo là triệu VND.

⚠️ Ba không gian mã, không cái nào thay được cái nào: codes= là mã lô (TPTTB2018/3Y — đừng viết hoa, 2.479 mã chứa ký tự ngoài [A-Z0-9]), issuer= là mã tổ chức của nguồn trái phiếu, symbol= là mã cổ phiếu. Ở issuers() thì codes= mang mã tổ chức.

⚠️ Gõ currency="VND", không phải "VNĐ" — nguồn lưu chuỗi có dấu, máy chủ dịch giúp, và gõ chuỗi của nguồn nhận một lỗi 400.


Chỉ báo kỹ thuật

135 hàm TA-Lib, ở hai tầng: df.finlens.* chạy trên DataFrame và tự tách theo mã, còn finlens.ta.* bám sát TA-Lib — mảng vào, mảng ra.

import finlens

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

df = df.finlens.rsi(14).finlens.macd().finlens.bbands(20)
print([c for c in df.columns if c not in ("symbol", "date")])
# ['open', 'high', 'low', 'close', 'volume',
#  'rsi_14', 'macd_12_26_9', 'macdsignal_12_26_9', 'macdhist_12_26_9',
#  'upperband_20_2_2_0', 'middleband_20_2_2_0', 'lowerband_20_2_2_0']

⚠️ Đây là lý do tầng df.finlens.* tồn tại. talib.RSI(df["close"]) trên một frame hai mã cho cửa sổ 14 phiên đầu của mã sau ăn 13 giá cuối của mã trước. Đo trên frame HPG+VCB 80 dòng: sai 40/80 dòng, mọi giá trị sai đều nằm trong khoảng 0–100 hợp lệ, không một cảnh báo nào. df.finlens.rsi(14) tự dò cột khoá (symbol, icb, code) và tính riêng từng nhóm; by=None nếu frame của bạn thật sự là một chuỗi giá duy nhất.

Mọi method trả về một bản sao kèm cột mới — không sửa tại chỗ, nên nối chuỗi được. Tên cột mang theo tham số, nên sma(20) và sma(50) là hai cột chứ không đè lên nhau.

Mẫu nến

df.finlens.patterns("doji")  # dạng DÀI, chỉ gồm các lần bắt được
df.finlens.patterns(["engulfing", "morningstar"], direction="tang")
df.finlens.pattern.cdldoji()  # dạng RỘNG, thêm một cột `cdldoji`

patterns() trả về symbol | date | pattern | ten_mau | signal | direction. Tên nhận cả "CDLDOJI", "cdldoji" và "doji".

⚠️ Cột signal không chỉ có ±100. CDLHIKKAKE và CDLHIKKAKEMOD ra thêm ±200 cho thanh xác nhận, nên df[df.signal == 100] âm thầm đánh rơi chúng. Lọc bằng df.signal > 0, hoặc dùng cột direction ("tang" / "giam") đã suy sẵn từ dấu.

⚠️ Quét cả 61 mẫu cho ra 1,8 dòng kết quả trên mỗi dòng đầu vào. Đo trên 500 mã × 1.500 phiên (750.000 dòng): 1.349.970 dòng, 2,22 giây, 312 MiB. Nêu tên mẫu trong which= cắt được 10 lần bộ nhớ, chứ không phải vài phần trăm.

Tầng bám sát TA-Lib

import finlens

close = df.loc[df["symbol"] == "HPG", "close"]

finlens.ta.RSI(close, timeperiod=14)  # Series vào → Series ra, giữ index
finlens.ta.MACD(close)  # tuple ba Series
finlens.ta.pattern.CDLDOJI(df["open"], df["high"], df["low"], df["close"])

Tên hàm, tên tham số và giá trị mặc định giữ nguyên của TA-Lib, nên code TA-Lib có sẵn chạy được sau khi đổi mỗi dòng import. Khác đúng ba chỗ, cả ba để chặn một cách hỏng im lặng:

  • Tham số là keyword-only. RSI(close, 14) ném TypeError. TA-Lib cho phép nó, và MACD(c, 26, 12, 9) thì đảo fastperiod với slowperiod rồi trả về một chỉ báo khác mà không báo gì.
  • Mọi lỗi là finlens.FinLensError. TA-Lib ném Exception trần.
  • timeperiod=14.5 bị từ chối. TA-Lib chạy và cắt phần thập phân trong im lặng, nên sau lời gọi không còn gì phân biệt được hai ý định đó.

74 chỉ báo ở finlens.ta, 61 mẫu nến ở finlens.ta.pattern. Tầng df.finlens.* có 73 chỉ báo — MAVP vắng mặt vì nó cần một mảng chu kỳ theo từng thanh chứ không phải một cột giá.

Bốn cái bẫy chung cho cả hai tầng

  • Warm-up không phải lỗi, và hai loại hàm biểu diễn nó khác nhau. Chỉ báo ra NaN (RSI với timeperiod=14 là đúng 14 dòng đầu mỗi mã); mẫu nến ra số 0, không phân biệt được với "đã quét và không có mẫu". Nhóm ngắn hơn warm-up ra toàn NaN / 0 và TA-Lib không báo lỗi — ở tầng df.finlens.* nó thành một finlens.DataQualityWarning.
  • NaN ở giữa chuỗi lan tới hết chuỗi, vĩnh viễn. Đo: một NaN ở dòng 30 của 60 làm dòng 30–59 toàn NaN. Thư viện cảnh báo chứ không tự chữa — ffill là bịa số, dropna là đổi cửa sổ, và cả hai là quyết định của người phân tích.
  • 24 trên 74 chỉ báo có trạng thái không ổn định, tức kết quả phụ thuộc chỗ bạn bắt đầu chuỗi. RSI(c)[200:] so với RSI(c[200:]) lệch tới 2,02 điểm, và phải tới phần tử thứ 85 chênh lệch mới xuống dưới 0,01. SMA thì không (lệch 7 × 10⁻¹⁴). Đổi start= của lời gọi dữ liệu là đổi con số bạn nhận về; docstring từng hàm nói rõ hàm nào.
  • Dữ liệu chưa sắp xếp theo thời gian cho ra một dãy số khác hẳn. Ở tầng df.finlens.* điều đó được xử lý — chỉ báo tính trên bản đã sắp rồi trả kết quả về đúng vị trí dòng gốc, kèm cảnh báo, và thứ tự dòng bạn nhận về không đổi. Ở tầng finlens.ta.* thì không có ai đứng giữa.

df.finlens được đăng ký khi finlens.accessor được nạp, và finlens.client() nạp nó. Nếu bạn dựng DataFrame từ file mà không tạo client thì cần import finlens.accessor một lần.

Đó là cái giá của một thứ đáng giữ: import finlens không kéo theo talib, pandas hay numpy — sau khi chạm cả vào finlens.ta, không tên nào trong ba tên đó có mặt trong sys.modules; chúng chỉ vào khi một hàm thật sự được gọi. Riêng import talib là khoảng nửa giây, và nó tự kéo pandas theo.


Đơ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 Cột giá Khối lượng Giá trị tiền
Cổ phiếu, ETF, chứng chỉ quỹ nghìn VND (22.3 = 22.300 đ) cổ phiếu VND
Chỉ số điểm chỉ số cổ phiếu —
Phái sinh điểm chỉ số hợp đồng VND
Chứng quyền VND thô chứng quyền VND

Luôn đọc thay vì giả định:

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

⚠️ DataFrame.attrs không sống sót qua pd.concat hay merge của pandas. Đọc đơn vị trước khi ghép frame.


Tra cứu nhanh

Bạn muốn Gọi
Giá VNINDEX theo tháng client.eod.index.ohlcv("VNINDEX", interval="1mo")
Khối ngoại mua ròng HPG client.eod.stock.investor.flow("HPG")
Từng lệnh khớp một phiên client.intraday.stock.ticks("HPG", date="2026-08-03")
Lệnh đặt vào sổ theo phiên client.eod.stock.supply_demand("HPG")
Chênh lệch VN30F1M với VN30 client.eod.derivative.basis("VN30F1M")
Cân đối kế toán theo quý client.financials.statement("HPG", kind="balance_sheet", period="quarterly")
Mọi mã ngành ngân hàng client.meta.symbols(icb="8300")
Chứng quyền còn hạn client.meta.warrants()
Giá thô, chưa điều chỉnh client.eod.stock.ohlcv("HPG", adjusted=False)
CPI, tỷ giá, lãi suất theo kỳ client.macro.series("ty_gia_trung_tam_daily")
Cán cân thương mại theo tháng client.macro.trade(flow="balance")
RSI, MACD trên frame nhiều mã df.finlens.rsi(14).finlens.macd()
Mẫu nến của cả frame df.finlens.patterns("engulfing")
Một chỉ báo trên một mảng finlens.ta.ATR(high, low, close, timeperiod=14)

Mọi phương thức đều nhận refresh=True để bỏ qua cache, và on_error="raise" để một mã lỗi làm cả lời gọi thất bại thay vì chỉ cảnh báo.


Xử lý lỗi

Mọi lỗi kế thừa finlens.FinLensError và mang theo .code, .request_id, .doc_url.

import finlens

try:
    df = client.eod.stock.ohlcv("HPG")
except finlens.RateLimitError as e:
    print(f"Chờ {e.retry_after} giây")
except finlens.DailyQuotaExceededError as e:
    print(f"Hết hạn mức ngày, mở lại lúc {e.resets_at}")
except finlens.InvalidSymbolError:
    print("Mã không tồn tại")
except finlens.FinLensError as e:
    print(f"{e.code}: {e}")

Cây ngoại lệ:

FinLensError
├── AuthError          InvalidApiKeyError · ApiKeyExpiredError · AccountExpiredError
├── TierError          DatasetNotInTierError · SymbolNotInTierError
├── QuotaError         RateLimitError · DailyQuotaExceededError
├── ValidationError    InvalidSymbolError · InvalidDateRangeError · InvalidIntervalError
├── TransportError     ConnectionFailedError · TlsVerificationError · RequestTimeoutError
└── DataError          SchemaMismatchError · NoDataError

ValidationError cũng kế thừa ValueError, nên except ValueError vẫn bắt được.

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ã thành công kèm một cảnh báo, chi tiết ở df.attrs["finlens"]["failed"].


Async

Mọi thứ có bản async với cùng chữ ký:

import asyncio
import finlens


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


asyncio.run(main())

Bản đồng bộ và bất đồng bộ dùng chung một lõi, nên không có chuyện một bên được sửa bug còn bên kia thì không.


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

Giá cổ phiếu tính bằng đơn vị gì? Nghìn đồng. 22.3 nghĩa là 22.300 VND. Chứng quyền thì ngược lại — VND thô. Luôn đọc df.attrs["finlens"]["units"].

Dữ liệu có từ năm nào? Giá cuối ngày cổ phiếu từ 2007, chỉ số ngành từ 2000, khối ngoại từ 2010, báo cáo tài chính từ 2004, tick trong phiên từ 2022. Nhóm nhà đầu tư chi tiết (cá nhân/tổ chức, trong nước/nước ngoài) từ 2024 và chỉ có ở HOSE.

Lấy được dữ liệu của phiên đang chạy không? Được. Dữ liệu trong phiên cập nhật liên tục và meta.as_of cho biết mốc nước. Lưu ý giá trong phiên là giá thô, chưa điều chỉnh quyền — khác với EOD.

Lấy được bao nhiêu mã một lần? Tuỳ gói dịch vụ, xem client.limits(). Thư viện tự chia nhỏ và gọi song song, bạn cứ truyền cả danh sách. Riêng ticks() là một mã một phiên.

Có chỉ báo kỹ thuật không? Có, 135 hàm TA-Lib — xem Chỉ báo kỹ thuật. ta-lib là phụ thuộc bắt buộc nên pip install finlens là đủ, không cần extra nào. Nếu bạn đã có sẵn code gọi talib thì đổi mỗi dòng import là chạy: tên hàm, tên tham số và giá trị mặc định giữ nguyên.

Có sổ lệnh (order book) không? Không. ticks() trả lệnh đã khớp, không phải độ sâu sổ lệnh. Giá đặt và khối lượng chờ theo bậc không có trong nguồn dữ liệu.

Có dữ liệu quỹ mở, trái phiếu, hàng hoá không? Chứng chỉ quỹ niêm yết (kind="fund") thì có. Giá hàng hoá và tỷ giá thì có, qua client.macro.series() — vàng giao ngay, dầu Brent, dầu WTI, tỷ giá trung tâm, lãi suất liên ngân hàng, lợi suất trái phiếu chính phủ:

client.macro.series("gia_vang_giao_ngay_daily")  # USD/Ounce
client.macro.series("dau_tho_brent_daily")  # USD/thùng

Quỹ mở, NAV, và giá từng mã trái phiếu doanh nghiệp thì chưa có.

Cache hoạt động thế nào? Tự động. Dữ liệu lịch sử cache 7 ngày, phiên gần nhất 60 giây, danh mục và cây ngành 24 giờ. refresh=True để bỏ qua, client.cache.stats() để xem.

Chạy sau proxy doanh nghiệp hoặc phần mềm diệt virus? Nếu gặp TlsVerificationError, trỏ tới CA bundle của tổ chức bạn: finlens.client(ca_bundle="/đường/dẫn/ca.pem") hoặc đặt biến môi trường FINLENS_CA_BUNDLE.


Nâng cấp từ 0.1.x

Phiên bản 1.0 là bản viết lại và có thay đổi phá vỡ tương thích. Danh sách đầy đủ nằm ở changelog. Đáng chú ý nhất:

  • Tên cột dùng snake_case — Date thành date.
  • interval="1M" bị từ chối vì nhập nhằng giữa một tháng và một phút; dùng 1mo hoặc 1min.
  • net_active_value() trước đây trả ba đơn vị khác nhau dưới cùng một tên cột; nay luôn là VND và có meta.value_unit khai rõ.
  • Mọi tham số sau mã chứng khoán là keyword-only.

Tài liệu và hỗ trợ

Tài liệu

Extension VS Code — dựng lời gọi bằng giao diện, xem trước dữ liệu ngay trong editor, và tạo khoá API mà không phải rời khỏi editor:

Báo lỗi và hỗ trợ

Khi mở issue, 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, và không có chúng thì gần như không lần lại được.

>>> finlens.build_info()
{'version': 'X.Y.Z', 'commit': 'a1b2c3d', 'built_at': '...', 'cython': '3.2.9', ...}

Giấy phép

MIT — xem toàn văn giấy phép. File LICENSE cũng đi kèm trong gói.

Metadata

Release files for finlens 1.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for finlens 1.5.0
File
finlens-1.5.0-cp311-abi3-win_amd64.whl CPython 3.11 abi3 Windows x86-64 Details
finlens-1.5.0-cp311-abi3-musllinux_1_2_x86_64.whl CPython 3.11 abi3 Linux musl 1.2+ x86-64 Details
finlens-1.5.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl CPython 3.11 abi3 Linux glibc 2.28+ ARM64, Linux glibc 2.17+ ARM64 Details
finlens-1.5.0-cp311-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl CPython 3.11 abi3 Linux glibc 2.28+ x86-64, Linux glibc 2.5+ x86-64 Details
finlens-1.5.0-cp311-abi3-macosx_14_0_arm64.whl CPython 3.11 abi3 macOS 14.0+ ARM64 Details
finlens-1.5.0-cp311-abi3-macosx_13_0_x86_64.whl CPython 3.11 abi3 macOS 13.0+ x86-64 Details

Total release size: 6.5 MB

Release files / finlens-1.5.0-cp311-abi3-win_amd64.whl

Download URL finlens-1.5.0-cp311-abi3-win_amd64.whl
Size 1.0 MB
Tags CPython 3.11 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
303c92c14e798e263f15d305f7e92be496eeed330193f90b99806ccb4cad3c2b
BLAKE2b-256 checksum
How to use checksums
c38459fde68b8e11047b15042cf1adf30806531e6d2d1b897686acaa8396eeab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release files / finlens-1.5.0-cp311-abi3-musllinux_1_2_x86_64.whl

Download URL finlens-1.5.0-cp311-abi3-musllinux_1_2_x86_64.whl
Size 1.2 MB
Tags CPython 3.11 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
66cd80b1cc24c873578a46561a9db939a01376c5ce6bda66010aa649a74ada73
BLAKE2b-256 checksum
How to use checksums
4f16d61d627e48a49c2825edeb52be2acf490662f04cf18d0d5fea43ae46a292
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release files / finlens-1.5.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

Download URL finlens-1.5.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Size 1.1 MB
Tags CPython 3.11 Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64 abi3
SHA-256 checksum
How to use checksums
a403510b19c7843fe5c456a1190f26aaa652a1c48c1b22ca9f60ecbede8d448e
BLAKE2b-256 checksum
How to use checksums
7435a1e0bcb0a6bd3f8a062b99eb95decea3a14e030f1545010ad7a486e3db85
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release files / finlens-1.5.0-cp311-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl

Download URL finlens-1.5.0-cp311-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl
Size 1.2 MB
Tags CPython 3.11 Linux glibc 2.28+ x86-64 Linux glibc 2.5+ x86-64 abi3
SHA-256 checksum
How to use checksums
c93845b88f1b6728fd85291921d021da3c103e35d632928a72320c51c60b3863
BLAKE2b-256 checksum
How to use checksums
92f99750123bd7931974d8388bd77ad83155ad17fdf11dc403faa0efae3ad4b2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release files / finlens-1.5.0-cp311-abi3-macosx_14_0_arm64.whl

Download URL finlens-1.5.0-cp311-abi3-macosx_14_0_arm64.whl
Size 998.3 kB
Tags CPython 3.11 abi3 macOS 14.0+ ARM64
SHA-256 checksum
How to use checksums
44e982b753867b3896775768eb4c0e300e208fe11a2477f3fd56804122d7aaa1
BLAKE2b-256 checksum
How to use checksums
c8559bcb3bec95efd0edf0bd8f6cebc269954595a93981f830ac46ca50b07d0a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release files / finlens-1.5.0-cp311-abi3-macosx_13_0_x86_64.whl

Download URL finlens-1.5.0-cp311-abi3-macosx_13_0_x86_64.whl
Size 1.0 MB
Tags CPython 3.11 abi3 macOS 13.0+ x86-64
SHA-256 checksum
How to use checksums
baa5369ecc6a73398a01700811b2c91f91b42dbc2a334822732eac64e881e813
BLAKE2b-256 checksum
How to use checksums
da8076a530830c76ef337750eba136aace9136ef6b732588bf4e63250f6ac673
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.5.0 This release

6 release files

1.4.0

6 release files

1.3.0

6 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page