Bot Python Chứng KhoánPythonTCBS APIDNSE APIChứng khoán
Được viết bởiĐặng Trí Thanhvào ngày 06/07/2026

Lập Trình Bot Giao Dịch Chứng Khoán Python Với API TCBS và DNSE

Hướng dẫn thực hành lập trình bot chứng khoán Python kết nối API TCBS và DNSE: lấy dữ liệu real-time, đặt lệnh tự động và monitor portfolio.

Lập Trình Bot Giao Dịch Chứng Khoán Python Với API TCBS và DNSE

Trong bài viết này, chúng ta đi vào thực hành: kết nối API chứng khoán Việt Nam (TCBS, DNSE), lấy dữ liệu giá real-time và gửi lệnh giao dịch tự động bằng Python.

Chuẩn Bị Môi Trường

pip install requests pandas numpy python-dotenv

Tạo file .env để lưu credentials an toàn — không bao giờ hard-code token trong code:

TCBS_TOKEN=your_token_here
DNSE_USERNAME=your_email
DNSE_PASSWORD=your_password

1. Lấy Dữ Liệu Giá Với TCBS API

Dữ Liệu Lịch Sử OHLCV

import requests
import pandas as pd
from datetime import datetime, timedelta

def get_historical_data(symbol: str, days: int = 365) -> pd.DataFrame:
    to_date = int(datetime.now().timestamp())
    from_date = int((datetime.now() - timedelta(days=days)).timestamp())

    url = (
        f"https://apipubaws.tcbs.com.vn/stock-insight/v1/stock/bars-long-term"
        f"?ticker={symbol}&type=stock&resolution=D"
        f"&from={from_date}&to={to_date}"
    )
    response = requests.get(url, timeout=10)
    data = response.json()

    df = pd.DataFrame(data['data'])
    df['date'] = pd.to_datetime(df['tradingDate'])
    return df.set_index('date').sort_index()

df_vnm = get_historical_data('VNM', days=365)
print(df_vnm.tail())

Giá Real-time

def get_realtime_price(symbol: str) -> dict:
    url = f"https://apipubaws.tcbs.com.vn/stock-insight/v1/stock/quotes?symbol={symbol}"
    r = requests.get(url).json()
    return {
        'symbol': symbol,
        'price': r['data'][0]['closePrice'],
        'volume': r['data'][0]['totalVolume'],
        'change_pct': r['data'][0]['priceChange']
    }

2. Đặt Lệnh Với DNSE API

DNSE cung cấp API đặt lệnh chính thức. Sau khi đăng ký và được cấp API access:

class DNSETrader:
    BASE_URL = "https://api.dnse.com.vn/v2"

    def __init__(self, access_token: str):
        self.headers = {
            "Authorization": f"Bearer {access_token}",
            "Content-Type": "application/json"
        }

    def place_order(self, symbol: str, side: str, quantity: int,
                    price: float, order_type: str = "LO") -> dict:
        payload = {
            "symbol": symbol,
            "side": side.upper(),
            "quantity": quantity,
            "price": price,
            "orderType": order_type
        }
        r = requests.post(f"{self.BASE_URL}/orders",
                          json=payload, headers=self.headers)
        return r.json()

3. Bot MA Crossover Tự Động Hoàn Chỉnh

import time

def is_trading_hours() -> bool:
    now = datetime.now()
    t = now.hour * 60 + now.minute
    morning = 9*60 <= t <= 11*60+30
    afternoon = 13*60 <= t <= 14*60+45
    return (morning or afternoon) and now.weekday() < 5

def run_bot(symbol: str, trader: DNSETrader):
    position = 0

    while True:
        if not is_trading_hours():
            time.sleep(60)
            continue

        df = get_historical_data(symbol, days=60)
        df['ema9'] = df['close'].ewm(span=9).mean()
        df['ema21'] = df['close'].ewm(span=21).mean()

        cross_up = (df['ema9'].iloc[-1] > df['ema21'].iloc[-1] and
                    df['ema9'].iloc[-2] < df['ema21'].iloc[-2])
        cross_down = (df['ema9'].iloc[-1] < df['ema21'].iloc[-1] and
                      df['ema9'].iloc[-2] > df['ema21'].iloc[-2])

        price = get_realtime_price(symbol)['price']

        if cross_up and position == 0:
            trader.place_order(symbol, 'buy', 100, price)
            position = 1
            print(f"BUY {symbol} @ {price}")
        elif cross_down and position == 1:
            trader.place_order(symbol, 'sell', 100, price)
            position = 0
            print(f"SELL {symbol} @ {price}")

        time.sleep(60)

Lưu Ý Bảo Mật Quan Trọng

  • Không push file chứa credentials lên GitHub
  • Dùng .gitignore để loại trừ .env
  • Giới hạn quyền API key — chỉ cấp quyền tối thiểu cần thiết
  • Monitor bot 24/7 và đặt cảnh báo Telegram khi có lệnh bất thường

Học kỹ hơn về lập trình bot chứng khoán thực chiến tại Hướng Nghiệp Dữ Liệu.

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

Làm sao lấy token TCBS hoặc DNSE cho bot Python?

Cả hai đều dùng luồng xác thực qua OTP hoặc mật khẩu để đổi lấy access token, sau đó dùng refresh token để gia hạn. Vì token có thời hạn, code phải lưu refresh token an toàn và tự động lấy token mới, nếu không bot sẽ chết giữa phiên giao dịch.

Có thể test bot chứng khoán mà không rủi ro tiền thật không?

Hầu hết công ty chứng khoán cung cấp môi trường sandbox hoặc tài khoản demo cho API, nhưng tính năng có thể hạn chế hơn bản thật. Cách an toàn nhất là chạy bot ở chế độ chỉ ghi log tín hiệu trong vài tuần để so sánh với dữ liệu thực tế trước khi bật đặt lệnh.

Bot chứng khoán Việt Nam nên chạy trên máy nào?

Nên chạy trên VPS hoặc server đám mây vì phiên giao dịch cố định 9h-15h, máy cá nhân dễ bị ngắt do mất điện hoặc mất mạng đúng lúc cần đặt lệnh. Cấu hình máy thấp cũng đủ vì bot chứng khoán không cần tốc độ mili giây như crypto.