尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證BCTC 30 mã VN30: lấy trực tiếp từ web công ty, OCR, Gemini so sánh quý, chấm điểm thang 10 + web dashboard
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Web: https://vietnhat-1305.github.io/bctc-vn30/
Web có 4 tab:
Web là một file HTML tĩnh (docs/index.html, đã chứa sẵn dữ liệu) do GitHub Pages phục vụ. Cập nhật web: python dang_web.py.
Rổ VN30 đang dùng là kỳ hiệu lực từ 03/08/2026, gồm 30 mã: ACB BID BSR CTG FPT GAS GVR HDB HPG LPB MBB MCH MSN MWG SAB SHB SSB SSI STB TCB TCX VCB VHM VIB VIC VJC VNM VPB VPL VRE.
| Bước | File | Việc |
|---|---|---|
| 1 | website_30cty.xlsx, LINKS.md, companies.json, kiem_tra_ir.py | Danh sách web đăng BCTC của 30 công ty và kết quả kiểm tra tool đọc được từng web |
| 2 | bctc.py, ir_adapters.py | Tải BCTC từ web công ty (nguồn đăng sớm nhất); CafeF chỉ để dự phòng |
| 3 | pdf2txt.py, ocr_so.py | PDF thường hoặc PDF scan → TXT (OCR tiếng Việt, giữ hàng/cột của bảng số). Trang báo cáo dạng scan được đọc lại bằng PP-OCRv6 để lấy số chính xác |
| 4 | phan_tich.py | Gemini đọc BCTC → DT, LNG, LNST, Tổng tài sản, Nợ, Nợ vay, Lưu chuyển tiền từ 1 BCTC: quý này so với cùng kỳ năm trước, cân đối so với đầu năm → Excel |
| 5 | cham_diem.py, dashboard.py | Chấm điểm thang 10 → dashboard mỗi công ty 1 thẻ, lọc nhanh (VD DT tăng > 20%, nợ vay giảm > 10%) |
pip install -r requirements.txt # aiohttp, pymupdf, google-genai, pydantic, openpyxl, cryptography
# Lấy API key Gemini miễn phí: https://aistudio.google.com/apikey -> dán vào file .env: GEMINI_API_KEY=...
python kiem_tra_ir.py # (tuỳ chọn) kiểm tra 30 web công ty còn đọc được không -> website_30cty.xlsx
python phan_tich.py # tải 1 BCTC quý mới nhất/mã từ web công ty -> Gemini -> chấm điểm -> đối chiếu Vietcap
# -> ket_qua/so_sanh_quy.xlsx + ket_qua/dashboard.html (mở bằng trình duyệt)
Lần sau chỉ cần chạy lại python phan_tich.py, rồi python dang_web.py để đưa kết quả lên web. BCTC và kết quả Gemini đã có được dùng lại, chỉ BCTC mới mới phải tải và hỏi Gemini.
python kiem_tra_ir.py đi vào web của từng công ty và kiểm tra 3 việc: đọc được danh sách BCTC, có BCTC quý mới nhất (hợp nhất), tải thử được file và đúng là file PDF. Kết quả ghi vào website_30cty.xlsx, mỗi công ty một dòng gồm: web, trang BCTC, cách tool đọc, PASS/FAIL, kỳ mới nhất trên web và trên CafeF, CafeF đăng chậm bao nhiêu ngày, link file mới nhất, ghi chú kỹ thuật.
Kết quả ngày 15/10/2026: PASS 30/30, đọc xong cả 30 web trong khoảng 20–35 giây.
Đo trên BCTC Q2/2026 ở 14 mã có đủ ngày để so: CafeF đăng sau web công ty trung vị 1,4 ngày, chậm nhất 5,5 ngày (GVR, SSI). Vì vậy nguồn chính là web công ty.
Chạy lại script này khi tool báo lỗi một mã, vì các web hay đổi giao diện.
python bctc.py # 30 mã, 2 năm gần nhất (mặc định --source ir-first)
python bctc.py --periods Q1,Q2,Q3,Q4 --latest 2 --scope HN # 2 BCTC quý hợp nhất gần nhất
python bctc.py --tickers VNM,FPT --years all # toàn bộ lịch sử (năm cũ web không còn thì lấy CafeF)
python bctc.py --list-only # chỉ lập danh mục link (data/index.csv)
python bctc.py --source cafef # chỉ dùng CafeF (nhanh nhất nhưng chậm hơn web công ty 1-5 ngày)
Mỗi web công ty dùng một kiểu khác nhau, nên tool có nhiều cách đọc. Cách đọc từng web nằm trong companies.json, xem bảng đầy đủ ở LINKS.md:
| Cách đọc | Các mã | Ghi chú |
|---|---|---|
| HTML tĩnh, có thể chia theo năm/quý | BID BSR CTG FPT GVR HPG SAB SSI VJC VNM VRE MCH MSN TCX VIB | VIB dùng User-Agent curl để qua Akamai; MSN, MCH, TCX đọc bằng curl vì Cloudflare chặn TLS của Python |
| API JSON | LPB STB TCB VCB VPB SHB | SHB đi qua Chrome headless vì Cloudflare chặn mọi request thường |
| 2 bước: danh sách → bài chi tiết → file | GAS SSB | |
Adapter riêng (ir_adapters.py) | ACB HDB MBB MWG | ACB: API theo mã danh mục năm; HDB: giải mã AES; MBB: token XSRF; MWG: danh mục động |
| Chrome headless (Cloudflare) | VHM VIC VPL SHB | Chrome hoặc Edge có sẵn trên máy, chạy ẩn, giữ cookie, tối đa 3 trang cùng lúc |
Luồng của một mã:
companies.json ──► đọc web công ty ──┐ (song song)
└► đọc CafeF ─────────┤
▼
phân loại: tên file + tiêu đề → (năm, kỳ Q1-Q4/6T/NAM, HN/RIENG, KT/SX); bỏ giải trình, công văn, thường niên
gộp IR-first: mỗi kỳ giữ 1 bản từ web công ty (ưu tiên tiếng Việt, "bản tra cứu" có sẵn chữ);
CafeF chỉ bù kỳ web không có; bản khác cùng kỳ làm dự phòng khi link hỏng
không nhận ra BCTC nào (web đổi giao diện) → AI dự phòng: Gemini đọc danh sách link trên trang và tự chọn
▼
tải song song (4 kết nối/host, stream, resume, kiểm tra %PDF, bỏ file trùng sha256) → data/<mã>/<năm>/
Số đo ngày 15/10/2026:
| Việc | Kết quả |
|---|---|
| Đọc danh mục 30 web công ty + CafeF | khoảng 12–35 giây (5 web cần Chrome mất 5–30 giây) |
| Tải 2 BCTC quý gần nhất của 30 mã từ web công ty | 61 file, 486 MB, 75 giây, 0 lỗi |
| Chạy lại khi không có BCTC mới | 12 giây |
| BCTC dùng cho phân tích lấy từ web công ty | 60/60 |
Tên file: data/VNM/2026/VNM_2026_Q2_HN.pdf = <mã>_<năm>_<kỳ>_<phạm vi>[_<kiểm toán>]. Kỳ: Q1–Q4, 6T (bán niên), NAM. Phạm vi: HN (hợp nhất), RIENG, CHUNG (công ty không có công ty con như LPB, TCX). Kiểm toán: KT, SX (soát xét).
python pdf2txt.py # mọi PDF trong data/ -> txt/ (giữ cấu trúc <mã>/<năm>/)
python pdf2txt.py data/ACB # một mã hoặc một file
python pdf2txt.py --pages 1-20 # chỉ 20 trang đầu (đủ chứa 3 báo cáo chính)
Khoảng 90% BCTC VN30 là PDF scan (đo trên 473 file: 427 file scan). Tool xét từng trang:
OCR dùng Tesseract có sẵn trong PyMuPDF, chỉ cần thư mục tessdata/, không phải cài gì. Bảng số giữ đúng hàng và cột. Tool chạy song song theo số nhân CPU thật; đo trên máy 6 nhân, 12 tiến trình còn chậm hơn 6.
ocr_so.py)Tesseract đọc dấu tiếng Việt tốt nhưng hay đọc sai chữ số (1 → 4, 6 → 8, cắt cụt số dài). Vì vậy các trang 3 báo cáo đã chọn được đọc lại bằng PP-OCRv6 của PaddleOCR (Apache-2.0), chạy qua RapidOCR + ONNX Runtime trên CPU, không cần GPU hay cài Paddle. Cấu hình: det small + rec small, lang_type="vi", ảnh 200 dpi, cạnh dài tối đa 2000 px, 6 luồng.
Đo ngày 18/10/2026 trên 5 BCTC scan (OCB, HCM, PNJ, DGC, KDH; 29 trang báo cáo). Đáp án là 145 con số đã được kiểm độc lập trên ảnh trang:
| Engine | Số đúng | Tốc độ |
|---|---|---|
Tesseract vie (tessdata_fast) | 134/145 (92,4%) | ~1,6 s/trang |
PP-OCRv6 small, vi | 145/145 (100%) | ~5–10 s/trang |
| PP-OCRv6 rec medium | như bản small trên trang thử | ~81 s/trang, bỏ |
Chia việc: Tesseract đọc nhanh cả file để tìm trang báo cáo; PP-OCRv6 chỉ đọc lại 4–16 trang báo cáo. Chữ của PP-OCRv6 hay mất dấu ("Tài sn ngn hn") và có lúc xếp số vào nhầm dòng ở bảng nhiều cột, nên AI vẫn đối chiếu với ảnh trang. Kết quả lưu cạnh txt của Tesseract: txt/<mã>/<năm>/<file>.ppocr.txt. Không cài rapidocr thì luồng tự dùng Tesseract như cũ.
pip install rapidocr onnxruntime # lần chạy đầu tự tải model (~30 MB)
python ocr_so.py data/PNJ/2026/PNJ_2026_Q2_HN.pdf 4-7 # OCR thử vài trang
python phan_tich.py # 30 mã
python phan_tich.py --tickers VNM,ACB,SSI
python phan_tich.py --dry-run # không gọi API: tải, chọn trang, lưu PDF nhỏ vào ket_qua/debug để xem trước
MỖI MÃ CHỈ 1 BCTC: quý mới nhất (công bố sớm nhất, có cột riêng quý) - báo cáo đã có sẵn kỳ so sánh
→ quét VÙNG TIÊU ĐỀ (30% đầu trang, ~0,25 s/trang) tìm Bảng CĐKT / KQKD / LCTT, bỏ mục lục, dừng ở Thuyết minh
→ OCR đầy đủ chỉ các trang báo cáo (4–16 trang thay vì 30–97)
→ PDF nhỏ (trang scan → JPEG xám 200 dpi) + văn bản OCR làm gợi ý
→ Gemini 3.5 Flash-Lite, structured output (JSON theo schema), temperature 0; prompt kèm SỔ BÀI HỌC (quy tắc + lưu ý từng mã)
→ kiểm tra chéo: TS = Nợ + VCSH (cuối quý và đầu năm), LCTT thuần = KD + ĐT + TC (năm nay và năm trước), số tháng lũy kế
→ tự sửa lỗi đã biết: LNST cổ đông mẹ không tách → bằng LNST; dòng LCTT in "-" → 0
→ TỰ ĐỐI CHIẾU VIETCAP IQ (doi_chieu.py): lệch > 1% → ghi vào sổ bài học để xét
→ cache ket_qua/json theo sha256 file + phiên bản sổ bài học (thêm quy tắc mới → trích lại theo quy tắc mới)
Một BCTC quý có đủ dữ liệu để so sánh, không cần BCTC quý trước:
| Báo cáo | Kỳ này | So với |
|---|---|---|
| Kết quả kinh doanh | Quý này (VD Q2/2026) và lũy kế từ đầu năm | Cùng quý năm trước (Q2/2025) và lũy kế cùng kỳ |
| Bảng cân đối | Cuối quý (30/06/2026) | Đầu năm (01/01/2026) - BCTC quý không có số cuối quý năm trước |
| Lưu chuyển tiền | Lũy kế từ đầu năm (6T/2026) | Lũy kế cùng kỳ năm trước (6T/2025) |
Số liệu Q2/2026 so với Q1/2026 đang có trên web do Claude (Anthropic) đọc. Cách làm như sau:
python phan_tich.py --dry-run cắt riêng các trang 3 báo cáo ra ket_qua/debug/.BCTC ở trên:
python claude_trich.py ghi <MÃ> 2026 <QUÝ> file.json. Lệnh này kiểm tra cân đối (Tổng TS = Nợ + VCSH, LCTT thuần = KD + ĐT + TC) rồi mới lưu vào ket_qua/json/.python phan_tich.py --tong-hop so sánh hai quý, chấm điểm và dựng web từ ket_qua/json/, không gọi AI.Đợt 16/10/2026 có 60/60 BCTC qua kiểm tra cân đối. Chỗ nào OCR đọc sai số thì đã sửa theo ảnh trang. Có vài điểm cần biết khi đọc số:
Có Gemini key thì chạy python phan_tich.py như bình thường. Kết quả Gemini sẽ ghi đè các quý mới.
AI không tự nhớ giữa các lần chạy, nên "học" nằm ở luồng:
tri_thuc/bai_hoc.json: quy tắc rút ra từ lỗi đã gặp (mẫu BCTC 2026 đổi mã số, ngân hàng không in dòng tổng
thu nhập, OCR đọc sai chữ số đầu, cùng kỳ trình bày lại...) và lưu ý riêng từng mã. Mỗi lần trích, quy tắc được đưa vào
system prompt, lưu ý của mã được đưa vào prompt của BCTC đó (cả Gemini lẫn Claude: claude_trich.py huong_dan).doi_chieu.py so từng chỉ tiêu (năm nay và kỳ so sánh) với Vietcap IQ, ghi
ket_qua/doi_chieu.json, thẻ trên web hiện "Đối chiếu Vietcap: x/y khớp". Chỗ lệch > 1% tự vào mục cần xem xét của sổ.python tri_thuc.py xem các mục cần xét; lỗi đọc thì python tri_thuc.py them-quy-tac "..." hoặc
them-luu-y MÃ "..." (sổ tăng phiên bản → lần chạy sau Gemini trích lại theo quy tắc mới); khác phiên bản báo cáo
(Vietcap dùng bán niên soát xét / năm kiểm toán cho quý 2, quý 4) thì python tri_thuc.py da-xet <số>.python doi_chieu.py # đối chiếu lại với Vietcap
python tri_thuc.py # xem sổ: quy tắc, lưu ý theo mã, mục cần xem xét
phan_tich.py chạy xong sẽ tự tạo:
ket_qua/dashboard.html: mở bằng trình duyệt, không cần mạng.ket_qua/so_sanh_quy.xlsx: gồm 3 sheet là Chấm điểm, So sánh quý và Dữ liệu từng BCTC.Muốn dựng lại dashboard mà không gọi API thì chạy python dashboard.py.
Dashboard gồm:
BCTC của ngân hàng, công ty chứng khoán, bảo hiểm và doanh nghiệp thường khác nhau về mẫu biểu, tên khoản mục và giới hạn an toàn pháp lý. Vì vậy mỗi loại hình có một bộ tiêu chí riêng. Bộ nào cũng tối đa 10 điểm và cho phép nửa điểm. Điểm do code tính từ số liệu, không phải AI tự cho.
| Loại hình | Khung tham chiếu | Chế độ BCTC | Nhóm (điểm tối đa) |
|---|---|---|---|
| Doanh nghiệp | Piotroski F-score | TT 99/2025/TT-BTC | Tăng trưởng 4 · Lợi nhuận 1 · Hiệu quả 1 · Dòng tiền 2 · Đòn bẩy & vốn 2 |
| Ngân hàng | CAMELS + IMF FSI | QĐ 16/2007/QĐ-NHNN (sửa bởi TT 49/2014, 22/2017, 27/2021) | C Vốn 1,5 · A Chất lượng tài sản 2 · E Hiệu quả 3,5 · L Thanh khoản 1 · Tăng trưởng 2 |
| Chứng khoán | Trần pháp lý (TT 121/2020, QĐ 87/QĐ-UBCK), IOSCO | TT 210/2014 + 334/2016/TT-BTC | Hiệu quả 3 · Tăng trưởng 2 · Chất lượng thu nhập 2 · Đòn bẩy & rủi ro 2 · Vốn 1 |
| Bảo hiểm phi nhân thọ | CARAMELS (IMF) + combined ratio | TT 232/2012/TT-BTC | Nghiệp vụ 3 · Đầu tư 2 · Hiệu quả 2 · Tăng trưởng 1,5 · Vốn & dự phòng 1,5 |
cham_diem.TIEU_CHI: mã, nhóm, công thức, ngưỡng, điểm, lý do, nguồn. Danh mục nguồn đã kiểm chứng nằm trong cham_diem.NGUON.python tai_lieu_cham_diem.py sinh 2 trang từ chính dữ liệu đó. dang_web.py tự gọi lệnh này mỗi lần dựng web.
docs/cham-diem.html: bộ tiêu chí chuẩn hoá. Gồm khoản mục lấy từ BCTC, công thức, thang điểm, lý do, nguồn, kèm ví dụ thật.docs/bao-cao-cham-diem.html: báo cáo vì sao chọn bộ tiêu chí này. Gồm so sánh bộ cũ và bộ mới, ngưỡng so với phân bố thực tế, các giới hạn còn lại, nguồn tham chiếu.nh_*, ck_*, bh_* trong schema). Với BCTC đã trích phần chung, bổ sung bằng python claude_trich.py bo_sung MÃ NĂM QUÝ file.json.ket_qua/diem_bo_cu_Q2_2026.json để báo cáo so sánh.Gồm 500 thuật ngữ thuộc 5 nhóm, mỗi nhóm khoảng 100: kinh tế vĩ mô, kế toán và BCTC, phân tích tài chính và định giá, ngân hàng và tín dụng, chứng khoán và đầu tư. Mỗi thuật ngữ có:
WIKI_BO_QUA, WIKI_BO_QUA_EN). Tool đi theo trang chuyển hướng, bỏ trang định hướng, và kiểm tra đúng chủ đề (ví dụ "CAR" dẫn sang bài Cộng hòa Trung Phi thì bị loại). Mỗi đoạn trích ghi nguồn và giấy phép CC BY-SA 4.0.python tu_dien.py # gộp tu_dien/nguon/*.json + tra Wikipedia (có cache) -> tu_dien/tu_dien.json + tu_dien_500.xlsx
python dang_web.py # đưa lên tab "Từ điển" của web
Muốn sửa hoặc thêm thuật ngữ thì sửa file trong tu_dien/nguon/ rồi chạy lại 2 lệnh trên.
{"ticker": "VNM", "fetch_mode": "static_html", "year_url": "https://...financial?year={year}"} // chia theo năm
{"ticker": "VIB", "fetch_mode": "static_html", "ua": "curl/8.4.0",
"year_url": "https://.../thong-tin-tai-chinh/{year}/bao-cao-quy-{q}?..."} // năm + quý
{"ticker": "SAB", "fetch_mode": "static_html", "bctc_url": "...", "year_links": "href=\"([^\"]*/(20\\d\\d)[^\"]*)\""}
{"ticker": "STB", "fetch_mode": "json_api", "api": {"url": "...json", "params": {"year": "{year}"}}}
{"ticker": "GAS", "fetch_mode": "two_step", "bctc_pages": ["<API danh sách>"], "follow_regex": "...", "href_regex": "..."}
{"ticker": "VHM", "fetch_mode": "static_html", "fetcher": "browser"} // Cloudflare
{"ticker": "HDB", "fetch_mode": "custom", "adapter": "hdb"} // ir_adapters.py
Các tuỳ chọn khác:
link_regex: link nằm trong JSON/JS, nhóm 1 là link.href_regex: link tải không có đuôi .pdf.page_is_bctc: trang chỉ đăng BCTC.title_from_row: lấy tiêu đề theo hàng chứa link.bctc_pages: liệt kê nhiều trang, có thể dùng {year}.fetcher: http (mặc định), curl hoặc browser.companies.json.kiem_tra_ir.py để phát hiện. Khi luật không còn nhận ra BCTC nào, AI dự phòng sẽ tự chọn link (cần API key).Ghi nguồn: tessdata/ (vie, eng) lấy từ tesseract-ocr/tessdata_fast, giấy phép Apache-2.0.