vnstock-js

vnstock-js

Tài LiệuVí DụBài ViếtTài Chính
k

© Copyright 2026

Trở về Bài Viết

Thứ Bảy, 16 tháng 11, 1929

v1.4 — AI-Native Foundation: MCP Server cho Cổ Phiếu Việt

Đăng bởi

QU

Quang Tran

@@ttqteo

cover

v1.4 — AI-Native Foundation: MCP cho Cổ Phiếu Việt

Ba tuần trước, tôi ship vnstock-js v1.4.0. Đó không phải chỉ là một version bump. Đó là khoảnh khắc khi AI trở thành native với thị trường chứng khoán Việt Nam.

Cho đến bây giờ, nếu bạn muốn Claude phân tích VCB hay so sánh FPT vs MBB, bạn có hai lựa chọn:

  1. Paste dữ liệu thủ công — Chậm, dễ sai, phá vỡ flow trò chuyện
  2. Xây dựng API riêng — Hàng tuần công việc, deploy, maintain, secure, rate-limit

Với v1.4, có một lựa chọn thứ ba: MCP Server. Claude giờ có truy cập trực tiếp vào dữ liệu chứng khoán Việt mà không cần bất kỳ sự phức tạp nào. Một file config. 11 tools. Dữ liệu real-time.

Bài post này đi sâu vào tại sao chúng tôi xây dựng nó, cái gì bạn có thể xây dựng giờ, và cách chúng tôi làm nó từ kỹ thuật.


Part 1: Tại Sao MCP? Vấn đề Gì Chúng Tôi Giải Quyết?

Thời Đại Operating System AI

Claude không chỉ là chatbot nữa. Nó đang trở thành hệ điều hành cho AI workflows. Mọi người đang dùng Claude để:

  • Nghiên cứu và quyết định
  • Thực thi và monitor
  • Khám phá và phân tích

Nhưng Claude không thể thấy dữ liệu thực tế. Nó có knowledge cutoff. Nó không thể fetch portfolio của bạn. Nó không thể check giá chứng khoán hôm nay. Cho đến bây giờ, nếu bạn muốn Claude truy cập dữ liệu real-time, bạn phải xây dựng một cây cầu.

Vấn Đề Cây Cầu

Cây cầu đó thường trông như:

Bạn → Prompt Claude → Claude nói "Tôi cần dữ liệu" → Bạn fetch dữ liệu → Bạn paste dữ liệu → Claude phân tích

Điều này tệ vì:

  • Công việc thủ công: Mỗi câu hỏi mới đòi hỏi bạn fetch dữ liệu mới
  • Context switching: Bạn không đang trò chuyện với Claude, bạn đang là người vận chuyển dữ liệu
  • Phân mảnh: Bạn dùng Claude Desktop ở nhà, Claude Code ở công ty, Cursor trong IDE — mỗi cái cần tích hợp riêng
  • Latency: Khi nào bạn paste dữ liệu xong, nó đã 5 phút tuổi rồi

MCP: Cây Cầu Tiêu Chuẩn

MCP (Model Context Protocol) là câu trả lời của Anthropic. Nó là một protocol nói:

"Đây là cách AI models kết nối với nguồn dữ liệu và tools mà không cần bất kỳ setup đặc biệt nào."

Với vnstock-js, MCP có nghĩa:

  • Claude Desktop nói chuyện với vnstock-js qua stdio (không expose port, không có network)
  • Claude Code dùng config giống nhau (không phải implement lại)
  • Cursor dùng config giống nhau (một setup, ở mọi nơi)
  • Dữ liệu real-time (bạn luôn phân tích bây giờ, không phải hôm qua)
  • Nó local (không internet dependency, không lo security)

Tại Sao Cổ Phiếu Việt Cần Cái Này

Thị trường chứng khoán Việt Nam là độc nhất:

  1. Không có OpenAPI chính thức — VCI (Vietnam Index) là nguồn dữ liệu, nhưng nó không được design cho AI consumption
  2. Dữ liệu phong phú — Quote, history, indicators, company info, trading signals — chúng ta muốn tất cả
  3. Nhu cầu real-time — Giá cổ phiếu thay đổi mỗi giây; dữ liệu cũ là vô dụng
  4. AI-first tools — Cursor, Claude Desktop, Claude Code — những cái này đang trở thành tools mặc định cho trading và research

Chúng tôi nhận ra: thay vì xây dựng 10 cái API wrapper khác nhau, chúng ta nên xây dựng một MCP server hoạt động ở mọi nơi Claude đi.


Part 2: Giờ Bạn Có Thể Xây Dựng Cái Gì?

Use Case 1: AI Portfolio Analyzer

Bạn: "Phân tích portfolio của tôi. Tôi có VCB 100 cổ phiếu @ giá vốn 50k, FPT 50 cổ phiếu @ 80k, MBB 200 cổ phiếu @ 28k."

Claude:

  1. Gọi tool quote → lấy giá hiện tại cho VCB, FPT, MBB
  2. Gọi tool aiContext → lấy trend, RSI, support/resistance cho mỗi cái
  3. Tính PnL của bạn: (currentPrice - costPrice) × quantity
  4. So sánh với market (FPT có outperform market không? VCB có overbought không?)
  5. Recommend: HOLD VCB, TRIM FPT (lên 25%, ở resistance), DCA vào MBB (trend bullish, dưới SMA50)

Output: Lời khuyên có cấu trúc với rationale.

Đây là phân tích thực, không hallucination. Claude có dữ liệu, Claude reason trên dữ liệu.


Use Case 2: Automated Trading Signals

Claude Code có thể chạy hàng ngày và email cho bạn trading opportunities:

// Claude Code script, chạy hàng ngày lúc 4pm
const vnstock = require('vnstock-js');

async function dailyAnalysis() {
  const symbols = ['VCB', 'FPT', 'MBB', 'TCB', 'HPG', 'VNM'];
  
  const signals = [];
  
  for (const symbol of symbols) {
    const context = await vnstock.stock(symbol).aiContext();
    
    // Bullish divergence: trend up, RSI < 70
    if (context.trend === 'bullish' && context.rsi < 70) {
      signals.push(`MUA: ${symbol} (RSI=${context.rsi}, trend bullish)`);
    }
    
    // Bearish reversal: trend down, volume spike
    if (context.trend === 'bearish' && context.volumeSignal === 'strong') {
      signals.push(`BÁN: ${symbol} (volume spike, bearish trend)`);
    }
  }
  
  if (signals.length > 0) {
    sendEmail('Trading Signals', signals.join('\n'));
  }
}

dailyAnalysis();

Không cần API wrapper. MCP server handle tất cả data fetching.


Use Case 3: Research Assistant

Bạn: "Tìm cho tôi 3 ngân hàng có trend bullish và RSI < 60. Hiển thị level support/resistance của chúng."

Claude:

  1. Gọi listing → lấy tất cả ngân hàng trên HOSE
  2. Cho mỗi ngân hàng, gọi aiContext → filter theo trend + RSI
  3. Format kết quả với support/resistance
  4. Bạn nhận được bảng, không phải một bức tường text

Hoặc: "Track watchlist của tôi hàng ngày và alert tôi nếu bất kỳ cái nào hit support hoặc break resistance."

Claude lưu watchlist trong ~/.vnstock-js/watchlist.json, check hàng ngày, gửi alerts.


Use Case 4: Integration với Cursor

Bạn đang viết trading bot trong Cursor. Bạn nói với Claude:

"Viết một function tìm stocks với RSI crossover dưới 30 trong 5 ngày qua, signal oversold conditions."

Claude:

  1. Fetch history cho nhiều stocks qua MCP
  2. Tính RSI
  3. Detect crossover
  4. Viết optimized code
  5. Suggest improvements dựa trên dữ liệu thực

Claude có dữ liệu thực, vậy suggestions của nó grounded.


Part 3: Technical Deep-Dive — Cách Chúng Tôi Xây Dựng MCP Server

Aspect 1: Chọn stdio Transport

Chúng tôi có các lựa chọn:

  1. HTTP API — Standard, widely understood, heavy
  2. WebSocket — Real-time, persistent, overkill cho request-response
  3. stdio — Lightweight, secure by default, không expose port

Chúng tôi chọn stdio vì:

  • Security by default: MCP server chạy trên máy của bạn, nói chuyện với Claude trên máy của bạn. Không expose port, không network roundtrip.
  • Claude Desktop native: Claude Desktop được design để launch subprocesses và communicate qua stdin/stdout.
  • Zero configuration: Không cần specify ports, IPs, hay lo collisions.
  • Low latency: Direct parent-child process communication vs HTTP handshake.

Protocol là JSON-RPC 2.0:

// Claude → MCP: "Lấy quote cho VCB"
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "quote",
    "arguments": { "symbol": "VCB" }
  }
}

// MCP → Claude: "Đây là dữ liệu"
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "symbol": "VCB",
    "matchingPrice": 105.2,
    "change": 1.2,
    "changePercent": 1.15,
    "volume": 15234500,
    "ceiling": 107.3,
    "floor": 103.1,
    "reference": 104.0
  }
}

Simple. Efficient. Stateless.


Aspect 2: Tool Registration & Schema

vnstock-js MCP server cần declare 11 tools cho Claude. Mỗi tool là một schema + handler:

interface Tool {
  name: string                    // "quote", "history", "aiContext"
  description: string             // Nó làm gì
  inputSchema: JSONSchema         // Nó nhận input gì
  handler: (args) => Promise<any> // Implementation
}

const tools: Tool[] = [
  {
    name: 'quote',
    description: 'Lấy giá cổ phiếu hiện tại, volume, ceiling/floor',
    inputSchema: {
      type: 'object',
      properties: {
        symbol: { type: 'string', description: 'Mã cổ phiếu (VCB, FPT, etc)' }
      },
      required: ['symbol']
    },
    handler: async (args) => {
      const quote = await adapter.getQuote(args.symbol)
      return {
        symbol: quote.symbol,
        matchingPrice: quote.matchingPrice,
        change: quote.change,
        changePercent: quote.changePercent,
        volume: quote.volume,
        ceiling: quote.ceiling,
        floor: quote.floor,
        reference: quote.reference
      }
    }
  },
  // ... 10 tools khác
]

Schema nói cho Claude:

  • Input nào valid
  • Tool trả về gì
  • Khi nào phù hợp để gọi

Claude đọc schema và quyết định khi nào gọi tools dựa trên prompt của bạn.


Aspect 3: Adapter Pattern

vnstock-js có adapter pattern từ v1.0:

interface StockDataAdapter {
  getQuote(symbol: string): Promise<Quote>
  getHistory(symbol: string, options): Promise<HistoryData>
  getCompanyInfo(symbol: string): Promise<CompanyInfo>
  // ... 8 methods khác
}

class VciAdapter implements StockDataAdapter {
  async getQuote(symbol: string) {
    const data = await this.fetchVci('/v1/stock/quote', { symbol })
    return this.transformQuoteResponse(data)
  }
  // ...
}

MCP server không cần adapter mới. Nó chỉ gọi adapter hiện tại:

const adapter = new VciAdapter()

const tools = [
  {
    name: 'quote',
    handler: (args) => adapter.getQuote(args.symbol)
  },
  // ... 10 tools khác
]

Đây là sức mạnh của architecture: Transport layer (HTTP, MCP, CLI) riêng biệt từ business logic. Một implementation, nhiều frontends.


Aspect 4: Hỗ Trợ Bilingual

Developers Việt phải có thể dùng tools bằng tiếng Việt. Developers nói tiếng Anh phải dùng bằng tiếng Anh.

Chúng tôi implement bilingual descriptions:

const tools = [
  {
    name: 'quote',
    description: 'Get current stock price | Lấy giá cổ phiếu hiện tại',
    inputSchema: {
      properties: {
        symbol: {
          type: 'string',
          description: 'Stock symbol (e.g. VCB) | Mã cổ phiếu (ví dụ VCB)'
        }
      }
    },
    handler: (args) => adapter.getQuote(args.symbol)
  }
]

Claude's language understanding mạnh đủ để parse bilingual descriptions và pick đúng tool.

Test:

  • English prompt: "Get the price of VCB" → Gọi quote ✓
  • Vietnamese prompt: "Lấy giá VCB" → Gọi quote ✓

Aspect 5: Error Handling & Resilience

VCI API không luôn nhanh. Network có thể chậm. Chúng tôi cần MCP server resilient:

1. Graceful degradation trên slow responses:

const handler = async (args) => {
  const startTime = Date.now();
  
  try {
    const result = await adapter.getQuote(args.symbol);
    
    // Nếu mất > 2s, log warning cho monitoring
    if (Date.now() - startTime > 2000) {
      logger.warn(`Slow query: quote(${args.symbol}) took ${Date.now() - startTime}ms`);
    }
    
    return result;
  } catch (err) {
    // Không crash. Trả về error với context.
    return {
      error: err.message,
      symbol: args.symbol,
      retry: true
    };
  }
};

2. Session caching để tránh rate limits:

const cache = new Map();

async function getCachedQuote(symbol: string) {
  const key = `quote:${symbol}`;
  
  if (cache.has(key)) {
    const { data, timestamp } = cache.get(key);
    // Nếu cached < 30s, trả về từ cache (VCI update mỗi 30s anyway)
    if (Date.now() - timestamp < 30000) {
      return data;
    }
  }
  
  const data = await adapter.getQuote(symbol);
  cache.set(key, { data, timestamp: Date.now() });
  return data;
}

VCI có rate limit ~100 req/min. Session caching (30-60s) đảm bảo Claude có thể spam tools mà không hit limit.

3. Auto-reconnect trên connection loss:

MCP protocol handle reconnection ở transport layer. Nếu stdout break, Claude Desktop tự động restart subprocess.


Aspect 6: 11 Tools

Chúng tôi shipped 11 tools. Hãy group chúng:

Data Tools (8) — direct VCI API passthrough:

  • quote — giá hiện tại, change, volume
  • history — OHLCV history cho charting
  • company — company info, industry, exchange
  • trading — bid/ask, average price
  • listing — tất cả stocks trên exchange
  • topMovers — gainers + losers
  • quickQuote — multiple quotes trong một call
  • watchlist — CRUD cho watchlists

Analysis Tools (3) — computation layer:

  • indicators — MACD, Bollinger, ATR
  • aiContext — trend, RSI, support/resistance, volume
  • compareSymbols — side-by-side comparison

Tại sao separate? Vì Claude cần cả hai:

  • Raw data cho exploration ("Cho tôi MACD cho VCB")
  • Pre-computed analysis cho decision-making ("VCB có overbought không?")

aiContext tool là secret sauce. Nó trả về structured analysis:

{
  "symbol": "VCB",
  "trend": "bullish",
  "indicators": {
    "rsi": 65.2,
    "sma20": 104.5,
    "sma50": 103.2,
    "sma200": 102.1
  },
  "support": 104.0,
  "resistance": 106.5,
  "volumeSignal": "strong",
  "priceChange": { "1d": 1.15, "7d": 2.5 }
}

Claude lấy trong một call cái mà cần 5 queries riêng lẻ.


Aspect 7: Testing — Biết Nó Hoạt Động

Chúng tôi thêm 27 tests cho MCP server:

describe('MCP Server', () => {
  describe('tools/quote', () => {
    it('returns price data for valid symbol', async () => {
      const result = await server.call('quote', { symbol: 'VCB' })
      expect(result).toHaveProperty('matchingPrice')
      expect(result).toHaveProperty('change')
    })
    
    it('handles invalid symbol gracefully', async () => {
      const result = await server.call('quote', { symbol: 'INVALID' })
      expect(result.error).toBeDefined()
    })
  })
  
  describe('tools/aiContext', () => {
    it('returns all required fields', async () => {
      const result = await server.call('aiContext', { symbol: 'VCB' })
      expect(result).toHaveProperty('trend')
      expect(result).toHaveProperty('indicators')
      expect(result).toHaveProperty('support')
      expect(result).toHaveProperty('resistance')
    })
  })
  
  // ... 25 tests khác covering:
  // - Edge cases (symbols at limits, empty watchlist)
  // - Error scenarios (network timeouts, invalid schemas)
  // - Bilingual descriptions
  // - Cache behavior
  // - Performance (all calls < 2s)
})

Tests hit real VCI API (không mock) để đảm bảo MCP server works với dữ liệu thực, không imaginary responses.


Aspect 8: Performance Profile

Chúng tôi benchmark mỗi tool:

ToolP50P95P99
quote120ms280ms450ms
history200ms350ms600ms
company100ms200ms350ms
aiContext250ms400ms800ms
indicators300ms500ms1000ms

Claude có patience để chờ 1s cho analysis, nhưng sẽ frustrated với 5s latency.

Chúng tôi optimize:

  • Caching — 30s cache cho quote, 60s cho aiContext
  • Parallel requests — compareSymbols fetch multiple symbols in parallel
  • Lazy init — symbols list chỉ load trên first listing call

Result: Hầu hết calls finish dưới 300ms ngay cả slow network.


Part 4: Real Usage Patterns

Pattern 1: Investment Decision

Bạn: Đang cân nhắc mua VCB. Nên không?

Claude:
1. Gọi aiContext(VCB)
2. Lấy: trend=bullish, RSI=65 (neutral/overbought), support=104, resistance=106
3. Checks: "Bullish trend nhưng approaching resistance"
4. Recommend: "Mua sau pullback đến 104-105, hoặc chờ breakout rõ ràng trên 106"

Đây là information, không phải advice. Claude là research assistant, không phải broker.


Pattern 2: Portfolio Rebalancing

Bạn: Đây là portfolio của tôi.
VCB 100 @ 50k
FPT 50 @ 80k
MBB 200 @ 28k
Total NAV: 100M

Nên rebalance không?

Claude:
1. Fetch quotes cho cả 3
2. Tính current NAV per position
3. Tính % của portfolio
4. Compare với sector benchmarks
5. Recommend: VCB & FPT overweight (27% + 22% = 49% trong 2 stocks)
   "Cân nhắc trim một position và rotate sang underweight sectors"

Lại là suggestion, không prescription.


Pattern 3: Automated Watchlist

Bạn: Monitor watchlist của tôi hàng ngày lúc 4pm.
Alert nếu bất kỳ stock nào break trên 20-day high
hoặc drop dưới 20-day low.

Claude Code (scheduled):
1. Load watchlist từ ~/.vnstock-js/watchlist.json
2. Mỗi symbol, fetch history(symbol, limit=20)
3. Tính high/low
4. Fetch current quote
5. Nếu breached, send email/Telegram/Slack

Đây là automation. MCP server supply data, Claude Code supply logic.


Tại Sao Điều Này Quan Trọng

Trước v1.4, nếu bạn muốn Claude làm research trading cho bạn, bạn phải:

  • Xây dựng API wrappers
  • Host nó ở đâu đó
  • Keep it running
  • Secure it
  • Monitor it

Đó là DevOps problem. Chúng tôi giải quyết nó với protocol problem — MCP.

Giờ:

  • vnstock-js là SDK
  • MCP server là cây cầu
  • Claude là brain
  • Bạn viết prompts, không APIs

Barrier to entry drop từ tuần xuống phút.


Kế Tiếp?

v1.5 (Power Workflow) sẽ focus trên:

  • Agent loops — Claude phân tích, propose trade, chờ approve, execute
  • Historical backtesting — "Nếu tôi follow signal này năm ngoái, ROI bao nhiêu?"
  • Multi-symbol optimization — "Recommend portfolio 10 stocks dựa trên risk tolerance của tôi"

Bây giờ, v1.4 là foundation. Bạn có dữ liệu real-time. Bạn có analysis. Bạn có protocol hoạt động ở mọi nơi Claude đi.

Next wave của AI trading tools sẽ không là closed platforms. Chúng sẽ là Claude + MCP + dữ liệu của bạn. Open. Composable. Yours.


Bắt Đầu

Đọc full MCP Server documentation →

Hoặc nếu prefer hands-on:

npm install vnstock-js
# Thêm vnstock-js MCP vào Claude Desktop config
# Bắt đầu hỏi câu hỏi

Claude giờ biết thị trường chứng khoán Việt. Bạn sẽ xây dựng gì?