npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vndirect-mcp

v0.2.0

Published

MCP server cung cấp dữ liệu chứng khoán Việt Nam (OHLC đa khung thời gian + chỉ số cơ bản) từ API công khai VNDIRECT.

Readme

vndirect-mcp

MCP server cá nhân cung cấp dữ liệu chứng khoán Việt Nam từ API công khai của VNDIRECT (không cần đăng nhập/API key), để dùng cho bot phân tích kỹ thuật trong VISSOFT AI Team Manager (hoặc bất kỳ Claude Code / MCP client nào khác).

Tool cung cấp

get_ohlc(symbol, resolution, count)

Lấy dữ liệu nến OHLCV lịch sử.

  • symbol: mã cổ phiếu, ví dụ HPG, VNM, FPT
  • resolution: "15m" | "1H" | "4H" | "1D"
  • count: số nến muốn lấy (1–500, mặc định 100)

Đã kiểm chứng hoạt động thật (test trực tiếp qua stdio JSON-RPC) cho cả 4 khung thời gian. Lưu ý:

  • 15m, 1H và 1D gọi thẳng API dchart-api.vndirect.com.vn (resolution 15 / 60 / D).
  • 4H không có sẵn ở VNDIRECT — server tự gộp mỗi 4 nến 1H liên tiếp thành 1 nến 4H (gộp theo thứ tự nến trả về, không theo mốc giờ tường minh, vì phiên giao dịch VN chỉ ~4.25h/ngày và ngắt quãng nghỉ trưa nên gộp theo giờ cố định dễ lệch hơn gộp tuần tự).
  • 15m cho phản ứng nhanh nhất (nhiều nhiễu hơn) — các ngưỡng của detect_macd_divergence/ compute_bar_metrics được giữ nguyên từ bản gốc tune trên khung thô hơn, riêng cửa sổ rolling của compute_bar_metrics ở 15m là giá trị ước lượng (chưa backtest thực tế cho khung này) — xem comment recentWindowFor() trong bar-metrics.ts.

get_fundamentals(symbol)

Lấy chỉ số/thông tin cơ bản qua api-finfo.vndirect.com.vn (lưu ý: đúng là api-finfo, KHÔNG PHẢI finfo-api — dễ gõ nhầm thứ tự, bản đầu tiên của tool này bị đúng lỗi này).

Đã kiểm chứng hoạt động thật với bộ 6 chỉ số curated (P/E, P/B, EPS 4 quý gần nhất, ROE, ROA, tỷ suất cổ tức) — test với HPG ra số hợp lý (P/E=8.02, P/B=1.32, EPS=2750đ, ROE=17.5%, ROA=9%).

Lịch sử debug endpoint (đáng đọc nếu endpoint VNDIRECT đổi lần nữa):

  • /v4/ratios/latest (bản đầu tiên dùng) hoạt động như 1 feed "bản ghi cập nhật gần nhất" — luôn trả đúng 1 dòng NGẪU NHIÊN theo order, bỏ qua hoàn toàn filter ratioCode — KHÔNG dùng được để lấy 1 chỉ số cụ thể.
  • Endpoint ĐÚNG là /v4/ratios (không có /latest), query param q (không phải filter), sort param sort=reportDate:desc (không phải order=reportDate) — mỗi ratioCode phải gọi RIÊNG 1 request (size=1&sort=reportDate:desc), vì danh sách nhiều ratioCode cách nhau dấu phẩy trong 1 lần gọi bị trộn không đáng tin (chỉ số cập nhật hàng ngày như P/E lấn át chỉ số cập nhật theo quý như ROAE trong cùng 1 trang kết quả).
  • Mã ratioCode xác nhận đúng: PRICE_TO_EARNINGS, PRICE_TO_BOOK, EPS_TR (EPS 4 quý liền kề — không phải EARNING_PER_SHARE/BASIC_EPS_TR như đoán ban đầu), ROAE_TR_AVG5Q, ROAA_TR_AVG5Q, DIVIDEND_YIELD.

Cảnh báo dữ liệu: dividendYield trả về giá trị cực nhỏ bất thường cho HPG (~1.8e-7, gần như 0) — có thể do công ty trả cổ tức chủ yếu bằng cổ phiếu (không phải tiền mặt) nên tỷ lệ tiền mặt/thị giá gần 0 là hợp lý thật, hoặc do cách VNDIRECT tính/đơn vị của field này khác kỳ vọng — CHƯA xác minh được nguyên nhân chính xác, dùng số này thận trọng, đối chiếu nguồn khác nếu user hỏi cụ thể về cổ tức.

render_chart(symbol, resolution, count, outputPath, smaPeriods?, showVolume?, showMacd?)

Vẽ biểu đồ nến kỹ thuật ra file PNG: candlestick + đường SMA chồng lên giá, panel MACD (12,26,9) với histogram + đường tín hiệu, panel khối lượng. Tự vẽ bằng @napi-rs/canvas (Canvas 2D API thô, không phụ thuộc Chart.js hay canvas gốc — xem "Ghi chú kỹ thuật" bên dưới).

  • outputPath: đường dẫn file .png sẽ ghi ra. Muốn bot gửi ảnh này qua Telegram trong lượt trả lời chat trực tiếp, path phải nằm trong thư mục scratch/ của bot (userData/bots/<botId>/scratch/) và bot phải in marker __BOT_SEND_FILE__{"kind":"photo","path":"..."}__BOT_SEND_FILE_END__ trong câu trả lời (cơ chế có sẵn của app, xem TASK-123). Cronjob tự động (cảnh báo watchlist) hiện CHƯA gửi được ảnh — đây là giới hạn ở tầng CronjobEngine/DeliveryService của app, đang track ở TASK-128 (docs/tasks/TASK-128 trong workspace-ai-team-central).
  • smaPeriods: mảng chu kỳ SMA muốn vẽ, tối đa 4 đường, mặc định [20, 50].
  • showVolume / showMacd: bật/tắt từng panel, mặc định true.

Đã kiểm chứng hoạt động thật — render thành công cả khung 1D và 1H với dữ liệu HPG/VNM thật, ảnh PNG hợp lệ, không lỗi. Layout 3-panel (giá/MACD/volume) tham khảo từ bot-trading-agent/scheduler/chart.py (dự án cũ của user, xem Notes).

Ghi chú kỹ thuật: ban đầu định dùng chart.js + chartjs-node-canvas + chartjs-chart-financial, nhưng package canvas (native, dependency của chartjs-node-canvas) không có prebuilt binary cho Node 24 trên Windows và build từ source cần Visual Studio Build Tools (không có sẵn trên máy). Đã chuyển sang @napi-rs/canvas (có prebuilt binary Windows x64) + tự vẽ chart thủ công bằng Canvas 2D API (candlestick = wick line + fillRect body, không dùng chart lib nào).

detect_macd_divergence(symbol, resolution, count)

Port TOÀN BỘ từ hệ thống alert engine cá nhân của user (D:\Projects\bot-trading\ bot-trading-agent, Python — indicators/extrema.py's find_lobe_extrema() + alerts/{divergence,confirmation,engine,config}.py), đã backtest + tune trên dữ liệu VNDIRECT thật (HPG/MBB). Đây là method THUẦN TÍNH TOÁN, không có bước "tự kết luận" mơ hồ như Wyckoff/VSA/SMC — nên port TOÀN BỘ (không tách lớp AI-diễn-giải), giữ nguyên mọi ngưỡng đã tune (alerts/config.py): cửa sổ xác nhận 7 phiên, thân nến ≥60% biên độ, volume ≥1.5x TB 10 phiên, tối thiểu 1 phiên "đi đúng hướng" để phân biệt weak-confirmation vs inconclusive.

Khác bản gốc: bản gốc là state machine PERSISTENT (lưu DB qua nhiều lần scheduler chạy). MCP tool không có state lưu trữ giữa các lần gọi — detect_macd_divergence() REPLAY lại toàn bộ lịch sử trong 1 lần gọi (walk-forward candle-by-candle, y hệt cách production xử lý từng nến mới) để tự tái tạo trạng thái hiện tại. Trả về: currentState (NEUTRAL/PENDING_CONFIRMATION), freshAlert (tín hiệu phát sinh ĐÚNG tại nến cuối — dùng để quyết định có cần cảnh báo ngay không), allAlerts (lịch sử đầy đủ trong khoảng dữ liệu, phục vụ ngữ cảnh).

Đã kiểm chứng hoạt động thật — test với HPG/MBB (đúng 2 mã bản gốc backtest), chuỗi early_divergence → failed_divergence/successful_reversal hợp lý, ngưỡng bucket đúng thiết kế (weak_confirmation_sessions chỉ xuất hiện khi pending_sessions đạt 7).

count mặc định 250, tối thiểu 60 — cần đủ lớn để thuật toán tìm được đủ lobe MACD trước đó (khuyến nghị giữ >= 200).

compute_bar_metrics(symbol, resolution, count) + find_trading_range(symbol, resolution, count) + analyze_smc_structure(symbol, resolution, count)

3 tool này port phần KHÁCH QUAN của Wyckoff/VSA/SMC từ 2 dự án cũ của user (D:\Projects\ai-factory\apps\wyckoffviet, Python — vsa_analysis.py, wyckoff_phase.py, smc_analysis_1h.py). Quyết định kiến trúc đã chốt: KHÔNG port bước "tự động gán nhãn kết luận" (vd code cũ: if volume≥2.5x avg và body≥50% range và close gần low: return "Selling Climax" — đây là DIỄN GIẢI theo phương pháp luận, tại sao 2.5x cụ thể chứ không phải 2.3x/2.7x là quyết định tuỳ ý của tác giả, không phải sự thật khách quan). Ranh giới áp dụng nhất quán cho cả 3 tool:

  • Port (sự thật hình học/số học, không có ngưỡng phương pháp luận tự đặt): swing high/low (pivot), trading range (vùng đi ngang), volume/spread ratio so với median, close position trong nến, BOS/CHoCH (giá phá đỉnh/đáy cũ — nhị phân, không % tự đặt), Fair Value Gap (định nghĩa hình học 3 nến, không tham số), Liquidity Sweep (wick xuyên rồi close quay lại — nhị phân).
  • KHÔNG port (diễn giải theo phương pháp luận, có ngưỡng tự đặt): gán nhãn signal VSA cụ thể (No Demand/Climax/Up-thrust/...), gán nhãn event Wyckoff cụ thể (SC/AR/ST/ Spring/SOS/LPS), phase classification (A→E), Order Block (ngưỡng body×1.5 tự đặt), bias/score tổng hợp, và đề xuất entry/SL/TP (tư vấn giao dịch cụ thể — phải do AI cân nhắc theo phong cách/khẩu vị rủi ro của user, không hard-code công thức chung).

AI (Claude) đọc dữ kiện khách quan từ 3 tool này + ảnh chart (render_chart) + skill mô tả phương pháp (xem skills/, đã viết), rồi tự diễn giải/kết luận — phần "phân tích" thật sự nằm ở đó.

Đã kiểm chứng hoạt động thật — test với HPG, cả 3 tool trả kết quả hợp lý (trading range 30 nến 16.23% biên độ, 10 swing highs/lows xen kẽ hợp lý, 4 FVG active, bar metrics với close_position/body_ratio đúng khoảng [0,1]).

Bug fix sau khi user báo lỗi thật (TCB): ban đầu find_trading_range trả null bất cứ khi nào KHÔNG có vùng nào đạt cả 2 ngưỡng (18% biên độ / 6% std) — với TCB (biên độ thật 21.79%, chỉ nhỉnh hơn ngưỡng chút), bot chỉ nói được "không tìm thấy", không có gì để phân tích tiếp dù dữ liệu thực ra khá gần đạt. Đã sửa: giờ LUÔN trả về 1 object (trừ khi <30 nến) — nếu không có vùng nào đạt cả 2 ngưỡng, trả về ứng viên GẦN NHẤT kèm qualifies: false + rangePct/stdPct thật để bot vẫn giải thích được "gần giống đi ngang nhưng chưa đủ chặt" thay vì im lặng. Verify lại: TCB → qualifies: false, rangePct: 21.79; HPG → vẫn qualifies: true như cũ (không regression).

compute_bar_metrics yêu cầu count >= 70 (median rolling 50 nến cần warm-up).

list_covered_warrants(underlyingSymbol, limit?) + analyze_covered_warrant(cwSymbol)

Tìm/so sánh chứng quyền có bảo đảm (covered warrant) cho 1 mã cơ sở — VNDIRECT chỉ niêm yết CW mua (call), chưa có CW bán.

  • list_covered_warrants: liệt kê CW còn niêm yết cho 1 mã cơ sở (mã CW, tổ chức phát hành, giá thực hiện, tỷ lệ chuyển đổi, ngày đáo hạn, số ngày còn lại) — dữ kiện TĨNH, chưa có giá thị trường. Endpoint /v4/derivatives?q=underlyingAsset:X~status:LISTED (endpoint /v4/covered_warrants cũ dùng trong wyckoffviet đã KHÔNG còn tồn tại — trả 404 khi test — phải tìm endpoint thay thế).
  • analyze_covered_warrant: phân tích 1 mã CW cụ thể — lấy giá CW + giá mã cơ sở hiện tại (tái dùng fetchCandles/dchart, hoạt động luôn cho mã CW vì CW cũng là 1 mã niêm yết giao dịch như cổ phiếu thường), tính breakeven/premium%/moneyness%/gearing lý thuyết — công thức tài chính chuẩn, đầy đủ, KHÔNG có ngưỡng "kết luận tốt/xấu" tự đặt nên port toàn bộ (giống detect_macd_divergence), không cần tách lớp AI-diễn-giải cho phần TÍNH TOÁN — nhưng việc chọn CW nào phù hợp vẫn cần AI cân nhắc khẩu vị rủi ro user (xem skill covered-warrant-method).

Đã kiểm chứng hoạt động thật — test với HPG (69 CW đang niêm yết), số liệu breakeven/ premium/moneyness/gearing đối chiếu tay đúng công thức.

Skill cho bot

skills/ chứa 6 skill (đúng format SKILL.md app dùng — id/name/description/ source: user/createdAt + nội dung markdown) — copy nội dung vào bot qua UI "Kỹ năng" (tạo skill riêng, dán nội dung), hoặc copy thẳng cả thư mục vào userData/bots/<botId>/.claude/skills/<id>/:

| Skill | Nội dung | |---|---| | stock-toolkit | Định hướng chung — tool nào dùng khi nào, quy trình phân tích, cách gửi ảnh chart qua Telegram | | wyckoff-method | Định nghĩa SC/AR/ST/Spring/SOS/LPS + cách suy ra phase A-E từ find_trading_range/compute_bar_metrics | | vsa-method | Định nghĩa các tín hiệu VSA (No Demand, Climax, Up-thrust...) + cảnh báo đọc trong strong trend | | smc-method | Cách đọc analyze_smc_structure + tự nhận diện Order Block + cân nhắc entry/SL/TP (không công thức cứng) | | macd-price-action | MA/MACD cổ điển, cách dùng detect_macd_divergence, Price Action cơ bản | | covered-warrant-method | Cách lọc/so sánh chứng quyền qua list_covered_warrants/analyze_covered_warrant — đọc premium/moneyness/gearing, không áp đặt 1 lựa chọn |

Nên bật cả 6 nếu muốn bot phân tích đầy đủ theo yêu cầu ban đầu (MA, MACD, Wyckoff, VSA, SMC, Price Action, chứng quyền) — hoặc chỉ bật vài skill nếu chỉ quan tâm 1-2 phương pháp.

Cài đặt & build

npm install
npm run build

Output nằm ở dist/index.js.

Cách wire vào app VISSOFT AI Team Manager

Đã publish lên npm — không cần clone/build thủ công nữa. Trong app, vào bot bạn muốn dùng (hoặc 📂 Tài nguyên của tôi > tab mcp), thêm 1 MCP server mới (loại command):

  • Command: npx
  • Args: ["-y", "vndirect-mcp"]

(-y để npx tự tải bản mới nhất không hỏi xác nhận — lần chạy đầu sẽ chậm hơn vài giây vì phải tải về, các lần sau dùng cache.)

Sau đó vào tab "Công cụ hỗ trợ (MCP)" của bot, tick chọn server này.

Chạy từ source (dev/debug): nếu muốn sửa code rồi test ngay không qua npm, dùng cấu hình cũ trỏ thẳng vào file build local:

  • Command: node
  • Args: ["<đường-dẫn-tuyệt-đối-tới-repo>/dist/index.js"]

Test thủ công (không qua app)

node dist/index.js

Server chạy stdio, chờ JSON-RPC trên stdin — dùng để debug tay hoặc tích hợp vào MCP client khác ngoài app này.