Chào các bạn, mình là Minh — một quantitative researcher đã làm việc với dữ liệu quyền chọn crypto hơn 4 năm. Hôm nay mình sẽ chia sẻ chi tiết cách lấy lịch sử quyền chọn Deribit bằng Python thông qua Tardis API, cùng với framework backtesting implied volatility (IV) hoàn chỉnh.

Bài viết này dành cho developer mới bắt đầu, không yêu cầu kinh nghiệm API trước đó. Tất cả code đều có thể copy-paste và chạy ngay.

Mục lục

1. Giới thiệu tổng quan

Deribit là gì?

Deribit là sàn giao dịch quyền chọn Bitcoin và Ethereum lớn nhất thế giới tính theo open interest. Nếu bạn muốn nghiên cứu hành vi thị trường quyền chọn crypto, Deribit là nguồn dữ liệu không thể bỏ qua.

Tardis API là gì?

Tardis API cung cấp dữ liệu lịch sử chất lượng cao từ các sàn crypto, bao gồm Deribit. Thay vì tự crawl dữ liệu (rất phức tạp và dễ bị chặn), bạn chỉ cần gọi API và nhận dữ liệu đã được chuẩn hóa.

Kinh nghiệm thực chiến: Mình đã thử tự crawl Deribit data 3 lần trước khi chuyển sang Tardis. Mất 2 tuần cho việc crawl, rồi phát hiện dữ liệu bị gap ở nhiều thời điểm. Tardis tiết kiệm cho mình ~40 giờ/tháng chỉ riêng phần thu thập và làm sạch dữ liệu.

Tại sao cần Implied Volatility (IV)?

Implied Volatility là chỉ số thị trường đo lường kỳ vọng biến động giá trong tương lai. Trong backtesting chiến lược quyền chọn, IV là yếu tố cốt lõi để:

2. Cài đặt môi trường

Yêu cầu hệ thống

Cài đặt thư viện

# Cài đặt các thư viện cần thiết
pip install tardis-client pandas numpy scipy requests asyncio aiohttp

Tạo file cấu hình

# config.py
import os

Tardis API Configuration

TARDIS_API_KEY = "your_tardis_api_key_here" # Đăng ký tại https://tardis.dev TARDIS_BASE_URL = "https://api.tardis.dev/v1"

Deribit Exchange Configuration

EXCHANGE = "deribit" INSTRUMENT_TYPE = "option" # hoặc "future", "spot"

Data Configuration

SYMBOLS = ["BTC-28APR23-28000-C", "ETH-30JUN23-2000-P"] # Ví dụ symbols START_DATE = "2023-01-01" END_DATE = "2023-12-31"

HolySheep AI Configuration (để xử lý dữ liệu với AI)

HOLYSHEEP_API_KEY = "YOUR_HOLYSHEEP_API_KEY" HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1"

3. Kết nối Tardis API

Kiểm tra kết nối

Trước khi lấy dữ liệu, chúng ta cần verify API key hoạt động tốt:

import requests
import json

def check_tardis_connection(api_key):
    """
    Kiểm tra kết nối Tardis API
    """
    url = "https://api.tardis.dev/v1/available-datasets"
    headers = {
        "Authorization": f"Bearer {api_key}"
    }
    
    try:
        response = requests.get(url, headers=headers)
        
        if response.status_code == 200:
            print("✅ Kết nối Tardis API thành công!")
            data = response.json()
            
            # Tìm datasets của Deribit
            deribit_datasets = [
                d for d in data.get("datasets", []) 
                if "deribit" in d.get("exchange", "").lower()
            ]
            
            print(f"\n📊 Số lượng datasets Deribit: {len(deribit_datasets)}")
            
            for ds in deribit_datasets[:5]:
                print(f"  - {ds.get('exchange')}: {ds.get('name')}")
            
            return True
        
        elif response.status_code == 401:
            print("❌ API Key không hợp lệ")
            return False
        else:
            print(f"❌ Lỗi: HTTP {response.status_code}")
            return False
            
    except Exception as e:
        print(f"❌ Lỗi kết nối: {e}")
        return False

Sử dụng

if __name__ == "__main__": API_KEY = "your_tardis_api_key" check_tardis_connection(API_KEY)

Trích xuất symbols Deribit

def get_deribit_symbols(api_key, market="option"):
    """
    Lấy danh sách tất cả symbols có sẵn trên Deribit
    """
    import requests
    
    url = f"https://api.tardis.dev/v1/derivatives/{market}"
    headers = {"Authorization": f"Bearer {api_key}"}
    params = {
        "exchange": "deribit",
        "limit": 1000,
        "offset": 0
    }
    
    all_symbols = []
    
    while True:
        response = requests.get(url, headers=headers, params=params)
        
        if response.status_code != 200:
            print(f"Lỗi HTTP: {response.status_code}")
            break
            
        data = response.json()
        symbols = data.get("results", [])
        
        if not symbols:
            break
            
        all_symbols.extend(symbols)
        
        if len(symbols) < 1000:
            break
            
        params["offset"] += 1000
        
    return all_symbols

Lấy danh sách quyền chọn BTC

btc_options = [s for s in get_deribit_symbols(TARDIS_API_KEY) if "BTC" in s.get("symbol", "") and s.get("type") == "option"] print(f"Tìm thấy {len(btc_options)} quyền chọn BTC")

4. Lấy dữ liệu quyền chọn Deribit

Tardis Client - Phương pháp đơn giản nhất

Tardis cung cấp Python client chính thức giúp đơn giản hóa việc lấy dữ liệu:

from tardis_client import TardisClient, exchanges, channels
import asyncio
from datetime import datetime, timedelta
import pandas as pd

async def fetch_options_data():
    """
    Lấy dữ liệu quyền chọn Deribit trong khoảng thời gian
    """
    # Khởi tạo client
    client = TardisClient(api_key="your_tardis_api_key")
    
    # Định nghĩa thời gian (UTC)
    from_timestamp = datetime(2023, 6, 1, 0, 0, 0)
    to_timestamp = datetime(2023, 6, 30, 23, 59, 59)
    
    # Lấy dữ liệu trade từ Deribit
    messages = client.tardis(
        exchange=exchanges.DERIBIT,
        from_timestamp=from_timestamp,
        to_timestamp=to_timestamp,
        channels=[channels.DERIBIT_TRADES],
        symbols=["BTC-30JUN23"]  # Filter theo symbol
    )
    
    trades_data = []
    
    # Iterate qua messages
    async for message in messages:
        if message.type == "trade":
            trades_data.append({
                "timestamp": message.timestamp,
                "symbol": message.symbol,
                "side": message.side,
                "price": float(message.price),
                "amount": float(message.amount),
                "iv": getattr(message, 'iv', None),  # Implied volatility nếu có
                "greeks": getattr(message, 'greeks', None)
            })
    
    return pd.DataFrame(trades_data)

Chạy async function

df_trades = asyncio.run(fetch_options_data()) print(f"Đã lấy {len(df_trades)} trades") print(df_trades.head())

Lấy dữ liệu Orderbook để tính IV

async def fetch_orderbook_for_iv():
    """
    Lấy orderbook để tính Implied Volatility
    Orderbook có bid/ask price → dùng để back-calculate IV
    """
    from tardis_client import TardisClient, exchanges, channels
    from scipy.optimize import brentq
    from scipy.stats import norm
    import numpy as np
    
    client = TardisClient(api_key="your_tardis_api_key")
    
    from_timestamp = datetime(2023, 7, 1, 0, 0, 0)
    to_timestamp = datetime(2023, 7, 1, 12, 0, 0)  # Chỉ 12 giờ đầu
    
    messages = client.tardis(
        exchange=exchanges.DERIBIT,
        from_timestamp=from_timestamp,
        to_timestamp=to_timestamp,
        channels=[channels.DERIBIT_BOOKSnapshot_100],
        symbols=["BTC-28JUL23-29000-C"]
    )
    
    orderbook_data = []
    
    async for message in messages:
        if hasattr(message, 'book'):
            bids = message.book.get('bids', [])
            asks = message.book.get('asks', [])
            
            if bids and asks:
                best_bid = float(bids[0][0])
                best_ask = float(asks[0][0])
                mid_price = (best_bid + best_ask) / 2
                
                orderbook_data.append({
                    "timestamp": message.timestamp,
                    "best_bid": best_bid,
                    "best_ask": best_ask,
                    "mid_price": mid_price,
                    "spread": best_ask - best_bid
                })
    
    return pd.DataFrame(orderbook_data)

df_book = asyncio.run(fetch_orderbook_for_iv())
print(f"Đã lấy {len(df_book)} snapshots")
print(df_book.head())

5. Tính Implied Volatility (IV)

Black-Scholes Model Implementation

import numpy as np
from scipy.stats import norm
from scipy.optimize import brentq
from datetime import datetime, timedelta

class BlackScholes:
    """
    Black-Scholes model để tính giá lý thuyết và Implied Volatility
    """
    
    @staticmethod
    def d1(S, K, T, r, sigma):
        """Tính d1 trong công thức Black-Scholes"""
        if T <= 0 or sigma <= 0:
            return np.nan
        return (np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    
    @staticmethod
    def d2(S, K, T, r, sigma):
        """Tính d2 trong công thức Black-Scholes"""
        if T <= 0 or sigma <= 0:
            return np.nan
        return BlackScholes.d1(S, K, T, r, sigma) - sigma * np.sqrt(T)
    
    @staticmethod
    def call_price(S, K, T, r, sigma):
        """Tính giá Call theo Black-Scholes"""
        if T <= 0:
            return max(0, S - K)
        d1 = BlackScholes.d1(S, K, T, r, sigma)
        d2 = BlackScholes.d2(S, K, T, r, sigma)
        return S * norm.cdf(d1) - K * np.exp(-r * T) * norm.cdf(d2)
    
    @staticmethod
    def put_price(S, K, T, r, sigma):
        """Tính giá Put theo Black-Scholes"""
        if T <= 0:
            return max(0, K - S)
        d1 = BlackScholes.d1(S, K, T, r, sigma)
        d2 = BlackScholes.d2(S, K, T, r, sigma)
        return K * np.exp(-r * T) * norm.cdf(-d2) - S * norm.cdf(-d1)
    
    @staticmethod
    def implied_volatility(market_price, S, K, T, r, option_type='call'):
        """
        Tính Implied Volatility từ giá thị trường
        
        Args:
            market_price: Giá thị trường của quyền chọn
            S: Giá underlying (BTC/ETH)
            K: Strike price
            T: Thời gian đến expiry (năm)
            r: Risk-free rate
            option_type: 'call' hoặc 'put'
        
        Returns:
            Implied Volatility (dạng thập phân, vd: 0.8 = 80%)
        """
        if T <= 0:
            return np.nan
            
        def objective(sigma):
            if option_type == 'call':
                theoretical = BlackScholes.call_price(S, K, T, r, sigma)
            else:
                theoretical = BlackScholes.put_price(S, K, T, r, sigma)
            return (theoretical - market_price) ** 2
        
        # Tìm IV bằng Brent method
        try:
            iv = brentq(objective, 0.001, 5.0)  # IV từ 0.1% đến 500%
            return iv
        except ValueError:
            return np.nan

Ví dụ sử dụng

bs = BlackScholes()

Giả sử có dữ liệu

S = 29000 # Giá BTC K = 28000 # Strike T = 0.1 # 10% năm ≈ 36.5 ngày r = 0.05 # Risk-free rate 5%

Tính giá Call với IV = 80%

call_price = bs.call_price(S, K, T, r, sigma=0.80) print(f"Giá Call (BS, IV=80%): ${call_price:.2f}")

Back-calculate IV từ giá thị trường

market_price = 3500 # Giá thị trường iv = bs.implied_volatility(market_price, S, K, T, r, 'call') print(f"Implied Volatility: {iv*100:.2f}%")

Tính IV từ dữ liệu Tardis

def calculate_iv_from_trades(df_trades, df_orderbook, df_underlying):
    """
    Tính Implied Volatility từ dữ liệu đã lấy từ Tardis
    
    Args:
        df_trades: DataFrame chứa trades
        df_orderbook: DataFrame chứa orderbook
        df_underlying: DataFrame chứa giá BTC/ETH
    """
    bs = BlackScholes()
    
    iv_results = []
    
    # Merge với giá underlying
    df_merged = df_orderbook.merge(
        df_underlying[['timestamp', 'price']], 
        on='timestamp', 
        how='left',
        suffixes=('_option', '_underlying')
    ).ffill()
    
    # Tính IV cho mỗi snapshot
    for idx, row in df_merged.iterrows():
        S = row['price_underlying']  # Giá BTC/ETH
        K = extract_strike_from_symbol(row.get('symbol', ''))  # Strike price
        T = calculate_time_to_expiry(row.get('expiry', ''))   # Thời gian đến expiry
        market_price = row['mid_price']
        
        if S and K and T and market_price:
            iv = bs.implied_volatility(market_price, S, K, T, r=0.05, option_type='call')
            iv_results.append({
                'timestamp': row['timestamp'],
                'iv': iv,
                'bid': row['best_bid'],
                'ask': row['best_ask'],
                'mid': row['mid_price'],
                'underlying_price': S
            })
    
    return pd.DataFrame(iv_results)

Ví dụ sử dụng

df_iv = calculate_iv_from_trades(df_trades, df_book, df_btc_price)

print(df_iv.head(10))

6. Xây dựng hệ thống Backtesting

Volatility Mean Reversion Strategy

import pandas as pd
import numpy as np
from datetime import datetime, timedelta

class VolatilityBacktester:
    """
    Hệ thống backtest chiến lược Volatility Mean Reversion
    """
    
    def __init__(self, initial_capital=100000, transaction_cost_pct=0.001):
        self.initial_capital = initial_capital
        self.transaction_cost_pct = transaction_cost_pct
        self.portfolio_value = initial_capital
        self.trades = []
        self.positions = {}
        self.equity_curve = []
        
    def run_backtest(self, df_iv, df_underlying, window=20, entry_threshold=1.5, exit_threshold=0.5):
        """
        Chạy backtest với chiến lược Mean Reversion
        
        Args:
            df_iv: DataFrame chứa Implied Volatility theo thời gian
            df_underlying: DataFrame chứa giá underlying
            window: Số ngày để tính trung bình di động
            entry_threshold: Ngưỡng z-score để vào lệnh (độ lệch so với mean)
            exit_threshold: Ngưỡng z-score để đóng lệnh
        """
        # Tính rolling mean và std của IV
        df_iv['iv_ma'] = df_iv['iv'].rolling(window=window).mean()
        df_iv['iv_std'] = df_iv['iv'].rolling(window=window).std()
        df_iv['iv_zscore'] = (df_iv['iv'] - df_iv['iv_ma']) / df_iv['iv_std']
        
        # Merge với giá underlying
        df = df_iv.merge(df_underlying[['timestamp', 'price']], on='timestamp', how='left')
        
        current_position = 0
        entry_price = 0
        entry_iv = 0
        
        for idx, row in df.iterrows():
            if pd.isna(row['iv_zscore']):
                continue
                
            timestamp = row['timestamp']
            iv = row['iv']
            iv_zscore = row['iv_zscore']
            underlying_price = row['price']
            
            # === KIỂM TRA ĐIỀU KIỆN VÀO LỆNH ===
            # Mua quyền chọn Call khi IV thấp bất thường (z-score < -entry_threshold)
            if current_position == 0 and iv_zscore < -entry_threshold:
                # Mở vị thế long Call
                position_value = self.portfolio_value * 0.1  # 10% capital
                num_contracts = position_value / (underlying_price * 0.1)  # Giả định multiplier
                
                cost = position_value * (1 + self.transaction_cost_pct)
                
                if cost <= self.portfolio_value:
                    current_position = num_contracts
                    entry_price = underlying_price
                    entry_iv = iv
                    self.portfolio_value -= cost
                    
                    self.trades.append({
                        'timestamp': timestamp,
                        'action': 'BUY',
                        'iv': iv,
                        'iv_zscore': iv_zscore,
                        'price': underlying_price,
                        'contracts': num_contracts,
                        'cost': cost,
                        'portfolio_value': self.portfolio_value
                    })
            
            # === KIỂM TRA ĐIỀU KIỆN RA LỆNH ===
            elif current_position > 0:
                # Đóng vị thế khi IV quay về mean (z-score > -exit_threshold)
                if iv_zscore > -exit_threshold:
                    revenue = current_position * underlying_price * 0.1 * (1 - self.transaction_cost_pct)
                    pnl = revenue - self.trades[-1]['cost']
                    
                    self.portfolio_value += revenue
                    current_position = 0
                    
                    self.trades.append({
                        'timestamp': timestamp,
                        'action': 'SELL',
                        'iv': iv,
                        'iv_zscore': iv_zscore,
                        'price': underlying_price,
                        'contracts': 0,
                        'revenue': revenue,
                        'pnl': pnl,
                        'portfolio_value': self.portfolio_value
                    })
            
            # Ghi nhận equity curve
            self.equity_curve.append({
                'timestamp': timestamp,
                'portfolio_value': self.portfolio_value + (current_position * underlying_price * 0.1 if current_position > 0 else 0)
            })
        
        # Đóng vị thế còn lại nếu chưa đóng
        if current_position > 0:
            last_price = df.iloc[-1]['price']
            revenue = current_position * last_price * 0.1 * (1 - self.transaction_cost_pct)
            self.portfolio_value += revenue
            self.trades.append({
                'action': 'FORCE_CLOSE',
                'pnl': revenue - self.trades[-1]['cost']
            })
        
        return self.calculate_metrics()
    
    def calculate_metrics(self):
        """Tính các metrics hiệu suất"""
        df_trades = pd.DataFrame(self.trades)
        df_equity = pd.DataFrame(self.equity_curve)
        
        if len(df_trades) == 0:
            return {}
        
        # Tính returns
        df_equity['returns'] = df_equity['portfolio_value'].pct_change()
        
        # Total Return
        total_return = (self.portfolio_value - self.initial_capital) / self.initial_capital * 100
        
        # Sharpe Ratio (annualized)
        annualization_factor = np.sqrt(365 * 24)  # Giả định hourly data
        sharpe_ratio = df_equity['returns'].mean() / df_equity['returns'].std() * annualization_factor
        
        # Maximum Drawdown
        df_equity['cummax'] = df_equity['portfolio_value'].cummax()
        df_equity['drawdown'] = (df_equity['portfolio_value'] - df_equity['cummax']) / df_equity['cummax']
        max_drawdown = df_equity['drawdown'].min() * 100
        
        # Win Rate
        closed_trades = df_trades[df_trades['action'] == 'SELL']
        if len(closed_trades) > 0:
            win_rate = (closed_trades['pnl'] > 0).sum() / len(closed_trades) * 100
        else:
            win_rate = 0
        
        return {
            'total_return': total_return,
            'final_capital': self.portfolio_value,
            'sharpe_ratio': sharpe_ratio,
            'max_drawdown': max_drawdown,
            'total_trades': len(df_trades),
            'win_rate': win_rate,
            'equity_curve': df_equity,
            'trades': df_trades
        }

Chạy backtest

backtester = VolatilityBacktester(initial_capital=100000)

metrics = backtester.run_backtest(df_iv, df_underlying)

print(f"Total Return: {metrics['total_return']:.2f}%")

print(f"Sharpe Ratio: {metrics['sharpe_ratio']:.2f}")

print(f"Max Drawdown: {metrics['max_drawdown']:.2f}%")

Sử dụng HolySheep AI để phân tích kết quả

Bạn có thể dùng HolySheep AI để xử lý và phân tích kết quả backtest nhanh hơn. Với chi phí chỉ $0.42/MTok (rẻ hơn 85%+ so với OpenAI), bạn có thể:

import requests
import json

def analyze_backtest_results_with_holysheep(api_key, metrics, df_trades_sample):
    """
    Sử dụng HolySheep AI để phân tích kết quả backtest
    
    Chi phí ước tính: ~$0.02 cho prompt này (rất rẻ!)
    Độ trễ: <50ms với HolySheep infrastructure
    """
    base_url = "https://api.holysheep.ai/v1"
    
    prompt = f"""
    Phân tích kết quả backtest chiến lược Volatility Mean Reversion:
    
    1. Total Return: {metrics.get('total_return', 0):.2f}%
    2. Sharpe Ratio: {metrics.get('sharpe_ratio', 0):.2f}
    3. Max Drawdown: {metrics.get('max_drawdown', 0):.2f}%
    4. Win Rate: {metrics.get('win_rate', 0):.2f}%
    5. Total Trades: {metrics.get('total_trades', 0)}
    
    Mẫu trades:
    {df_trades_sample.head(10).to_string()}
    
    Đưa ra:
    1. Đánh giá tổng quan chiến lược
    2. Các điểm cần cải thiện
    3. Đề xuất thay đổi tham số
    """
    
    payload = {
        "model": "deepseek-v3.2",  # Model rẻ nhất, $0.42/MTok
        "messages": [
            {"role": "system", "content": "Bạn là chuyên gia phân tích quantitative trading. Trả lời bằng tiếng Việt."},
            {"role": "user", "content": prompt}
        ],
        "temperature": 0.7,
        "max_tokens": 1000
    }
    
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }
    
    response = requests.post(
        f"{base_url}/chat/completions",
        headers=headers,
        json=payload
    )
    
    if response.status_code == 200:
        result = response.json()
        return result['choices'][0]['message']['content']
    else:
        return f"Lỗi: {response.status_code}"

Sử dụng

analysis = analyze_backtest_results_with_holysheep(

HOLYSHEEP_API_KEY,

metrics,

metrics['trades']

)

print(analysis)

7. Lỗi thường gặp và cách khắc phục

Trong quá trình làm việc với Tardis API và dữ liệu quyền chọn, mình đã gặp nhiều lỗi. Dưới đây là tổng hợp các lỗi phổ biến và cách fix.

Lỗi 1: Lỗi xác thực API (401 Unauthorized)

# ❌ SAI - Key không đúng hoặc đã hết hạn
TARDIS_API_KEY = "invalid_key_123"

✅ ĐÚNG - Kiểm tra và xác thực lại

import os def validate_tardis_api_key(api_key): """Validate API key trước khi sử dụng""" if not api_key: raise ValueError("API Key không được để trống") if len(api_key) < 20: raise ValueError("API Key có vẻ không hợp lệ (quá ngắn)") # Kiểm tra format if not api_key.replace("-", "").replace("_", "").isalnum(): raise ValueError("API Key chứa ký tự không hợp lệ") # Test kết nối test_url = "https://api.tardis.dev/v1/available-datasets" headers = {"Authorization": f"Bearer {api_key}"} response = requests.get(test_url, headers=headers) if response.status_code ==