Xin chào, mình là Minh — một lập trình viên đã dành 3 năm nghiên cứu về giao dịch tự động và phân tích dữ liệu tiền mã hóa. Hôm nay, mình muốn chia sẻ với bạn một hành trình mà mình ước ai cũng biết từ sớm: cách lấy dữ liệu lịch sử từ sàn OKX để thực hiện backtest chiến lược giao dịch.

Nếu bạn là người mới hoàn toàn, đừng lo lắng. Bài viết này được viết cho người không biết gì về API cả. Mình sẽ giải thích từng khái niệm, từng dòng code, để bạn có thể bắt đầu ngay hôm nay.

OKX API là gì và tại sao cần nó?

Trước khi đi vào chi tiết kỹ thuật, mình muốn bạn hiểu OKX API giống như một "cầu nối" cho phép máy tính tự động lấy dữ liệu từ sàn giao dịch OKX. Thay vì bạn ngồi chụp ảnh màn hình giá Bitcoin mỗi ngày, API sẽ làm điều đó tự động và chính xác.

OKX là một trong những sàn giao dịch tiền mã hóa lớn nhất thế giới với:

Việc lấy dữ liệu lịch sử (historical data) phục vụ cho backtest — một kỹ thuật cho phép bạn "chạy thử" chiến lược giao dịch với dữ liệu quá khứ trước khi bỏ tiền thật ra thị trường.

Bạn cần chuẩn bị những gì?

1. Tài khoản OKX

Nếu bạn chưa có tài khoản OKX, hãy đăng ký tại okx.com. Quá trình đăng ký đơn giản và hoàn toàn miễn phí.

2. Tạo API Key

Sau khi có tài khoản, bạn cần tạo API Key để truy cập dữ liệu:

  1. Đăng nhập vào OKX → vào Account Settings
  2. Chọn tab API Management
  3. Click Create API Key
  4. Đặt tên cho API Key (ví dụ: "BacktestBot")
  5. Chọn quyền: chỉ cần Read (đọc dữ liệu) là đủ
  6. Lưu lại API Key, Secret KeyPassphrase

⚠️ Lưu ý quan trọng: KHÔNG BAO GIỜ chia sẻ Secret Key cho ai. Nếu bạn chỉ cần đọc dữ liệu (không giao dịch), hãy chỉ tạo key với quyền đọc để tránh mất tiền.

3. Cài đặt Python

Mình khuyên bạn nên sử dụng Python 3.8 trở lên. Đây là ngôn ngữ phổ biến nhất trong lĩnh vực phân tích dữ liệu tài chính.

# Cài đặt các thư viện cần thiết
pip install requests pandas numpy matplotlib

Hoặc cài đặt tất cả trong một dòng

pip install requests pandas numpy matplotlib pandas-datareader

Hướng dẫn lấy dữ liệu từ OKX API

Phần 1: Kết nối cơ bản với OKX

Bây giờ mình sẽ hướng dẫn bạn viết code đầu tiên để lấy dữ liệu. Đừng lo nếu bạn chưa biết lập trình — mình sẽ giải thích từng dòng.

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

============================================

CẤU HÌNH API OKX - THAY THẾ BẰNG THÔNG TIN CỦA BẠN

============================================

API_KEY = "YOUR_OKX_API_KEY" SECRET_KEY = "YOUR_OKX_SECRET_KEY" PASSPHRASE = "YOUR_OKX_PASSPHRASE"

Base URL của OKX API

BASE_URL = "https://www.okx.com" def get_server_time(): """Lấy thời gian hiện tại từ server OKX (bắt buộc cho việc signing)""" url = f"{BASE_URL}/api/v5/market/time" response = requests.get(url) return response.json()['data'][0]['ts'] def create_signature(timestamp, method, request_path, body=""): """ Tạo chữ ký số cho request Đây là cách OKX xác thực request của bạn """ import hmac import hashlib import base64 message = timestamp + method + request_path + body mac = hmac.new( SECRET_KEY.encode('utf-8'), message.encode('utf-8'), digestmod=hashlib.sha256 ) return base64.b64encode(mac.digest()).decode('utf-8') print("✅ Kết nối OKX API thành công!") print(f"Server time: {get_server_time()}")

Phần 2: Lấy dữ liệu lịch sử OHLCV

OHLCV là viết tắt của Open (Giá mở), High (Giá cao nhất), Low (Giá thấp nhất), Close (Giá đóng), Volume (Khối lượng) — đây là 5 thông tin cơ bản nhất của mỗi nến giao dịch.

def get_historical_klines(symbol, interval, start_time, end_time=None, limit=100):
    """
    Lấy dữ liệu lịch sử nến từ OKX
    
    Args:
        symbol: Mã cặp giao dịch (VD: "BTC-USDT")
        interval: Khung thời gian nến ("1H", "4H", "1D", v.v.)
        start_time: Thời gian bắt đầu (định dạng timestamp milliseconds)
        end_time: Thời gian kết thúc (mặc định: hiện tại)
        limit: Số lượng nến tối đa mỗi request (tối đa: 100)
    
    Returns:
        DataFrame chứa dữ liệu OHLCV
    """
    
    # Mapping các khung thời gian
    interval_map = {
        "1m": "1m", "5m": "5m", "15m": "15m",
        "1H": "1H", "4H": "4H", "6H": "6H",
        "1D": "1D", "1W": "1W"
    }
    
    url = f"{BASE_URL}/api/v5/market/history-candles"
    
    params = {
        "instId": symbol,
        "bar": interval_map.get(interval, "1D"),
        "limit": min(limit, 100)
    }
    
    if end_time:
        params["after"] = end_time
    else:
        params["before"] = start_time
    
    try:
        response = requests.get(url, params=params)
        data = response.json()
        
        if data.get('code') != '0':
            print(f"❌ Lỗi API: {data.get('msg')}")
            return None
        
        candles = data['data']
        
        # Chuyển đổi dữ liệu thành DataFrame
        df = pd.DataFrame(candles, columns=[
            'timestamp', 'open', 'high', 'low', 'close', 'volume', 
            'quote_volume', 'turnover', 'confirm'
        ])
        
        # Chuyển đổi timestamp
        df['datetime'] = pd.to_datetime(df['timestamp'].astype(float), unit='ms')
        
        # Chuyển đổi các cột số
        for col in ['open', 'high', 'low', 'close', 'volume', 'quote_volume']:
            df[col] = pd.to_numeric(df[col], errors='coerce')
        
        return df.sort_values('datetime').reset_index(drop=True)
        
    except Exception as e:
        print(f"❌ Lỗi kết nối: {str(e)}")
        return None

============================================

VÍ DỤ: LẤY DỮ LIỆU BTC/USDT KHUNG 1 NGÀY

============================================

print("Đang tải dữ liệu Bitcoin...")

Lấy dữ liệu 365 ngày gần nhất

end_time = int(time.time() * 1000) start_time = end_time - (365 * 24 * 60 * 60 * 1000) df_btc = get_historical_klines( symbol="BTC-USDT", interval="1D", start_time=start_time, limit=100 ) if df_btc is not None: print(f"✅ Đã tải {len(df_btc)} ngày dữ liệu Bitcoin") print("\n📊 5 dòng dữ liệu đầu tiên:") print(df_btc[['datetime', 'open', 'high', 'low', 'close', 'volume']].head()) else: print("❌ Không thể tải dữ liệu")

Phần 3: Lấy dữ liệu nhiều cặp giao dịch cùng lúc

Trong thực tế, bạn sẽ cần dữ liệu của nhiều đồng coin để xây dựng danh mục đa dạng. Đoạn code sau giúp bạn tự động lấy dữ liệu của Top 10 đồng coin theo vốn hóa:

def get_multiple_coins_data(coins, interval="1D", days=365):
    """
    Lấy dữ liệu lịch sử của nhiều cặp giao dịch
    
    Args:
        coins: Danh sách các cặp giao dịch
        interval: Khung thời gian
        days: Số ngày dữ liệu cần lấy
    
    Returns:
        Dictionary chứa DataFrame của từng cặp
    """
    all_data = {}
    end_time = int(time.time() * 1000)
    
    for i, coin in enumerate(coins):
        print(f"Đang tải {coin}... ({i+1}/{len(coins)})")
        
        df = get_historical_klines(
            symbol=coin,
            interval=interval,
            start_time=end_time - (days * 24 * 60 * 60 * 1000),
            limit=100
        )
        
        if df is not None:
            all_data[coin] = df
            print(f"  ✅ {len(df)} ngày dữ liệu")
        else:
            print(f"  ❌ Không tải được {coin}")
        
        # Tránh spam API - nghỉ 0.5 giây giữa các request
        time.sleep(0.5)
    
    return all_data

============================================

VÍ DỤ: LẤY DỮ LIỆU TOP 5 COIN

============================================

top_coins = [ "BTC-USDT", # Bitcoin "ETH-USDT", # Ethereum "BNB-USDT", # Binance Coin "SOL-USDT", # Solana "XRP-USDT" # Ripple ] print("=" * 50) print("BẮT ĐẦU TẢI DỮ LIỆU NHIỀU CẶP GIAO DỊCH") print("=" * 50) all_data = get_multiple_coins_data(top_coins, interval="1D", days=180)

Lưu dữ liệu ra file CSV

for coin, df in all_data.items(): filename = f"data_{coin.replace('-', '_')}.csv" df.to_csv(filename, index=False) print(f"💾 Đã lưu: {filename}") print("\n✅ Hoàn thành tải dữ liệu!")

Phần 4: Trực quan hóa dữ liệu

Để hiểu rõ hơn về dữ liệu, mình sẽ hướng dẫn bạn vẽ biểu đồ giá và khối lượng giao dịch:

import matplotlib.pyplot as plt
import matplotlib.dates as mdates

def plot_price_chart(df, title="Giá Bitcoin", save_path="chart.png"):
    """
    Vẽ biểu đồ giá và khối lượng
    
    Args:
        df: DataFrame chứa dữ liệu OHLCV
        title: Tiêu đề biểu đồ
        save_path: Đường dẫn lưu file
    """
    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 8), 
                                    gridspec_kw={'height_ratios': [3, 1]})
    
    # Biểu đồ giá
    ax1.plot(df['datetime'], df['close'], color='#F7931A', linewidth=1.5)
    ax1.fill_between(df['datetime'], df['close'], alpha=0.3, color='#F7931A')
    ax1.set_title(title, fontsize=14, fontweight='bold')
    ax1.set_ylabel('Giá (USD)', fontsize=11)
    ax1.grid(True, alpha=0.3)
    ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
    
    # Biểu đồ khối lượng
    ax2.bar(df['datetime'], df['volume'], color='#4169E1', alpha=0.7, width=1)
    ax2.set_ylabel('Khối lượng', fontsize=11)
    ax2.set_xlabel('Thời gian', fontsize=11)
    ax2.grid(True, alpha=0.3)
    ax2.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
    
    plt.tight_layout()
    plt.savefig(save_path, dpi=150, bbox_inches='tight')
    plt.show()
    print(f"📈 Biểu đồ đã lưu tại: {save_path}")

============================================

VẼ BIỂU ĐỒ BITCOIN

============================================

if df_btc is not None: plot_price_chart( df_btc, title="Giá Bitcoin (USD) - 365 ngày gần nhất", save_path="btc_price_chart.png" )

Xây dựng hệ thống Backtest đơn giản

Bây giờ bạn đã có dữ liệu, mình sẽ hướng dẫn bạn xây dựng một hệ thống backtest cơ bản để đánh giá chiến lược giao dịch.

import numpy as np

class SimpleBacktester:
    """
    Hệ thống backtest đơn giản cho chiến lược Moving Average Crossover
    """
    
    def __init__(self, df, initial_balance=10000):
        """
        Khởi tạo backtester
        
        Args:
            df: DataFrame chứa dữ liệu giá
            initial_balance: Số dư ban đầu (USD)
        """
        self.df = df.copy()
        self.initial_balance = initial_balance
        self.balance = initial_balance
        self.position = 0  # Số lượng coin đang nắm giữ
        self.trades = []
        
    def add_indicators(self, short_period=20, long_period=50):
        """
        Thêm các chỉ báo kỹ thuật
        """
        self.df['SMA_short'] = self.df['close'].rolling(window=short_period).mean()
        self.df['SMA_long'] = self.df['close'].rolling(window=long_period).mean()
        return self
    
    def run_strategy(self):
        """
        Chạy chiến lược SMA Crossover:
        - MUA khi SMA ngắn cắt lên SMA dài
        - BÁN khi SMA ngắn cắt xuống SMA dài
        """
        for i in range(len(self.df)):
            row = self.df.iloc[i]
            
            if pd.isna(row['SMA_short']) or pd.isna(row['SMA_long']):
                continue
            
            # Tín hiệu MUA: SMA ngắn cắt lên SMA dài
            if (i > 0 and 
                self.df.iloc[i-1]['SMA_short'] <= self.df.iloc[i-1]['SMA_long'] and
                row['SMA_short'] > row['SMA_long'] and
                self.balance > 0):
                
                # Mua với toàn bộ số dư
                self.position = self.balance / row['close']
                self.balance = 0
                self.trades.append({
                    'type': 'BUY',
                    'date': row['datetime'],
                    'price': row['close'],
                    'balance': 0,
                    'position': self.position
                })
            
            # Tín hiệu BÁN: SMA ngắn cắt xuống SMA dài
            elif (i > 0 and 
                  self.df.iloc[i-1]['SMA_short'] >= self.df.iloc[i-1]['SMA_long'] and
                  row['SMA_short'] < row['SMA_long'] and
                  self.position > 0):
                
                # Bán toàn bộ position
                self.balance = self.position * row['close']
                self.trades.append({
                    'type': 'SELL',
                    'date': row['datetime'],
                    'price': row['close'],
                    'balance': self.balance,
                    'position': 0
                })
                self.position = 0
        
        # Tính giá trị cuối cùng
        final_value = self.balance + self.position * self.df.iloc[-1]['close']
        return final_value
    
    def calculate_metrics(self):
        """
        Tính toán các chỉ số hiệu suất
        """
        final_value = self.balance + self.position * self.df.iloc[-1]['close']
        total_return = (final_value - self.initial_balance) / self.initial_balance * 100
        
        # Đếm số giao dịch
        num_trades = len(self.trades)
        buy_trades = [t for t in self.trades if t['type'] == 'BUY']
        sell_trades = [t for t in self.trades if t['type'] == 'SELL']
        
        # Tính win rate (so sánh giá mua với giá bán gần nhất)
        wins = 0
        for i, trade in enumerate(self.trades):
            if trade['type'] == 'SELL':
                # Tìm giá mua gần nhất
                buy_prices = [t['price'] for t in self.trades[:i] if t['type'] == 'BUY']
                if buy_prices and trade['price'] > buy_prices[-1]:
                    wins += 1
        
        win_rate = wins / len(sell_trades) * 100 if sell_trades else 0
        
        return {
            'initial_balance': self.initial_balance,
            'final_value': final_value,
            'total_return': total_return,
            'num_trades': num_trades,
            'num_wins': wins,
            'win_rate': win_rate,
            'trades': self.trades
        }

============================================

CHẠY BACKTEST CHO BITCOIN

============================================

print("=" * 60) print("BACKTEST CHIẾN LƯỢC SMA CROSSOVER CHO BITCOIN") print("=" * 60)

Khởi tạo và chạy backtest

backtester = SimpleBacktester(df_btc, initial_balance=10000) backtester.add_indicators(short_period=20, long_period=50) backtester.run_strategy()

Lấy kết quả

results = backtester.calculate_metrics() print(f"\n📊 KẾT QUẢ BACKTEST") print(f"{'='*40}") print(f"💰 Số dư ban đầu: ${results['initial_balance']:,.2f}") print(f"💰 Giá trị cuối cùng: ${results['final_value']:,.2f}") print(f"📈 Tổng lợi nhuận: {results['total_return']:.2f}%") print(f"🔢 Tổng số giao dịch: {results['num_trades']}") print(f"✅ Tỷ lệ thắng: {results['win_rate']:.1f}%")

Vẽ biểu đồ kết quả

fig, ax = plt.subplots(figsize=(14, 6)) ax.plot(backtester.df['datetime'], backtester.df['close'], label='Giá', alpha=0.7) ax.plot(backtester.df['datetime'], backtester.df['SMA_short'], label='SMA 20', alpha=0.7) ax.plot(backtester.df['datetime'], backtester.df['SMA_long'], label='SMA 50', alpha=0.7)

Đánh dấu điểm mua/bán

for trade in results['trades']: if trade['type'] == 'BUY': ax.scatter(trade['date'], trade['price'], color='green', s=100, marker='^', zorder=5) else: ax.scatter(trade['date'], trade['price'], color='red', s=100, marker='v', zorder=5) ax.set_title('Chiến lược SMA Crossover - Tín hiệu Mua/Bán', fontsize=14) ax.set_xlabel('Thời gian') ax.set_ylabel('Giá (USD)') ax.legend() ax.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('backtest_result.png', dpi=150) plt.show()

So sánh OKX API với các sàn khác

Tiêu chí OKX Binance CoinGecko (Free)
Dữ liệu lịch sử ✅ Đầy đủ, nhiều năm ✅ Đầy đủ ⚠️ Giới hạn 90 ngày
Giới hạn API 20 request/2s 1200 request/1min 30-50 request/phút
Độ trễ ~50-100ms ~50-100ms ~200-500ms
Độ tin cậy ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐
Phí giao dịch 0.08% (Maker) 0.1% (Maker) Miễn phí (chỉ đọc)

Phù hợp / không phù hợp với ai

✅ NÊN sử dụng khi:

❌ KHÔNG nên sử dụng khi:

Giá và ROI

Nếu bạn định sử dụng các công cụ AI để phân tích dữ liệu và xây dựng chiến lược, chi phí có thể là một yếu tố quan trọng. Dưới đây là bảng so sánh chi phí khi sử dụng các API AI phổ biến:

Dịch vụ Giá/Million tokens Phương thức thanh toán Khả năng phân tích dữ liệu
GPT-4.1 (OpenAI) $8.00 Thẻ quốc tế ⭐⭐⭐⭐⭐
Claude Sonnet 4.5 (Anthropic) $15.00 Thẻ quốc tế ⭐⭐⭐⭐⭐
Gemini 2.5 Flash (Google) $2.50 Thẻ quốc tế ⭐⭐⭐⭐
DeepSeek V3.2 $0.42 Thẻ quốc tế ⭐⭐⭐⭐
HolySheep AI $0.42 (tương đương DeepSeek) WeChat/Alipay/Thẻ ⭐⭐⭐⭐⭐

💡 Phân tích ROI:

Vì sao chọn HolySheep AI?

Trong quá trình nghiên cứu về giao dịch tự động, mình đã thử nghiệm nhiều nền tảng AI khác nhau. HolySheep AI nổi bật với những ưu điểm mà mình đánh giá rất cao: