Giới thiệu tổng quan
Trong lĩnh vực tài chính phái sinh tiền mã hóa, việc phân tích dữ liệu期权链 (chuỗi quyền chọn) và资金费率 (tỷ lệ tài trợ) là yếu tố then chốt để xây dựng chiến lược giao dịch hiệu quả. Bài viết này sẽ hướng dẫn chi tiết cách sử dụng Tardis CSV — bộ dữ liệu lịch sử chất lượng cao cho phép nghiên cứu sâu về cấu trúc thị trường phái sinh. Đối với các nhà phân tích muốn ứng dụng AI vào quy trình xử lý và diễn giải dữ liệu này, HolySheep AI cung cấp nền tảng với độ trễ dưới 50ms và chi phí chỉ từ $0.42/MTok với DeepSeek V3.2, giúp tiết kiệm đến 85% so với các giải pháp truyền thống.Tardis CSV là gì và tại sao nó quan trọng
1.1 Khái niệm cơ bản
Tardis là nền tảng thu thập và lưu trữ dữ liệu lịch sử từ nhiều sàn giao dịch tiền mã hóa. Khác với các API thời gian thực, Tardis CSV cho phép bạn tải về các bộ dữ liệu hoàn chỉnh phục vụ nghiên cứu backtesting và phân tích retrospective.Ưu điểm của Tardis CSV: Độ phủ đa sàn (Binance, Bybit, OKX, Deribit), định dạng chuẩn hóa, khả năng xử lý hàng triệu dòng dữ liệu, và tài liệu API chi tiết.
1.2 Phạm vi dữ liệu hỗ trợ
Tardis cung cấp nhiều loại dữ liệu phái sinh quan trọng:- 期权链 (Options Chain): Dữ liệu quyền chọn với giá strike, ngày hết hạn, premium, và delta
- 资金费率 (Funding Rates): Tỷ lệ tài trợ theo chu kỳ 8 giờ của hợp đồng vĩnh cửu
- Kline/OHLCV: Dữ liệu nến với khung thời gian linh hoạt
- Orderbook: Sổ lệnh chi tiết với bid/ask levels
- Giao dịch spot: Dữ liệu thị trường giao ngay
Cài đặt và cấu hình môi trường
2.1 Yêu cầu hệ thống
Trước khi bắt đầu, đảm bảo môi trường của bạn đã cài đặt các thư viện cần thiết:# Cài đặt các thư viện cần thiết
pip install pandas numpy matplotlib seaborn requests python-dotenv
Thư viện chuyên dụng cho phân tích tài chính
pip install scipy statsmodels
Cài đặt SDK Tardis (nếu sử dụng API trực tiếp)
pip install tardis-client
Kiểm tra phiên bản
python --version # Python 3.9+ được khuyến nghị
pip list | grep -E "pandas|tardis"
2.2 Cấu trúc thư mục dự án
Tổ chức cấu trúc thư mục giúp quản lý dữ liệu hiệu quả:# Cấu trúc thư mục đề xuất
project/
├── config/
│ └── settings.py # Cấu hình API keys
├── data/
│ ├── raw/ # Dữ liệu thô từ Tardis
│ ├── processed/ # Dữ liệu đã xử lý
│ └── analysis/ # Kết quả phân tích
├── notebooks/ # Jupyter notebooks
├── scripts/
│ ├── fetch_tardis.py # Script tải dữ liệu
│ ├── analyze_options.py # Phân tích quyền chọn
│ └── analyze_funding.py # Phân tích funding rate
├── utils/
│ ├── data_loader.py # Hàm load dữ liệu
│ └── calculations.py # Công thức tính toán
└── requirements.txt
Tạo cấu trúc tự động
mkdir -p project/{config,data/{raw,processed,analysis},notebooks,scripts,utils}
touch project/config/.env
Tải và xử lý dữ liệu Tardis CSV
3.1 Đăng ký và lấy API Key từ Tardis
Để sử dụng Tardis, bạn cần đăng ký tài khoản và lấy API key. Tardis cung cấp gói dùng thử với giới hạn data points nhất định.# File: scripts/fetch_tardis.py
import os
import pandas as pd
from dotenv import load_dotenv
from datetime import datetime, timedelta
import requests
import time
load_dotenv('config/.env')
class TardisDataFetcher:
"""
Class kết nối và tải dữ liệu từ Tardis
Hỗ trợ: Options Chain, Funding Rates, OHLCV
"""
BASE_URL = "https://api.tardis.dev/v1"
def __init__(self, api_key: str = None):
self.api_key = api_key or os.getenv('TARDIS_API_KEY')
if not self.api_key:
raise ValueError("TARDIS_API_KEY không được tìm thấy trong biến môi trường")
def fetch_options_chain(self, exchange: str, symbol: str,
start_date: str, end_date: str) -> pd.DataFrame:
"""
Tải dữ liệu chuỗi quyền chọn (Options Chain)
Args:
exchange: Sàn giao dịch (binance, bybit, deribit, okex)
symbol: Cặp tiền (BTC, ETH)
start_date: Ngày bắt đầu (YYYY-MM-DD)
end_date: Ngày kết thúc (YYYY-MM-DD)
Returns:
DataFrame chứa dữ liệu quyền chọn
"""
url = f"{self.BASE_URL}/export/derivatives/options"
params = {
'exchange': exchange,
'symbol': symbol,
'start_date': start_date,
'end_date': end_date,
'api_key': self.api_key,
'format': 'csv'
}
print(f"🔄 Đang tải dữ liệu Options Chain từ {exchange}...")
start_time = time.time()
response = requests.get(url, params=params, timeout=120)
response.raise_for_status()
# Lưu file CSV tạm thời
temp_file = f"data/raw/options_{exchange}_{symbol}_{start_date}_{end_date}.csv"
with open(temp_file, 'wb') as f:
f.write(response.content)
df = pd.read_csv(temp_file)
elapsed = (time.time() - start_time) * 1000
print(f"✅ Hoàn thành trong {elapsed:.2f}ms | {len(df)} dòng dữ liệu")
return df
def fetch_funding_rates(self, exchange: str, symbols: list,
start_date: str, end_date: str) -> pd.DataFrame:
"""
Tải dữ liệu tỷ lệ tài trợ (Funding Rates)
"""
all_data = []
for symbol in symbols:
url = f"{self.BASE_URL}/export/derivatives/funding-rates"
params = {
'exchange': exchange,
'symbol': symbol,
'start_date': start_date,
'end_date': end_date,
'api_key': self.api_key,
'format': 'csv'
}
print(f"🔄 Đang tải Funding Rate: {symbol}")
response = requests.get(url, params=params, timeout=60)
response.raise_for_status()
temp_file = f"data/raw/funding_{exchange}_{symbol}.csv"
with open(temp_file, 'wb') as f:
f.write(response.content)
df = pd.read_csv(temp_file)
all_data.append(df)
combined_df = pd.concat(all_data, ignore_index=True)
combined_df = combined_df.sort_values('timestamp')
return combined_df
Sử dụng class
if __name__ == "__main__":
fetcher = TardisDataFetcher()
# Tải dữ liệu quyền chọn BTC từ Deribit
options_df = fetcher.fetch_options_chain(
exchange='deribit',
symbol='BTC',
start_date='2024-01-01',
end_date='2024-03-01'
)
# Tải funding rates từ Binance Futures
funding_df = fetcher.fetch_funding_rates(
exchange='binance',
symbols=['BTCUSDT', 'ETHUSDT'],
start_date='2024-01-01',
end_date='2024-03-01'
)
3.2 Xử lý và làm sạch dữ liệu
Dữ liệu thô từ Tardis thường cần được làm sạch trước khi phân tích:# File: utils/data_loader.py
import pandas as pd
import numpy as np
from datetime import datetime
from typing import Tuple, List
class OptionsDataProcessor:
"""
Xử lý và làm sạch dữ liệu quyền chọn từ Tardis CSV
"""
def __init__(self, df: pd.DataFrame):
self.df = df.copy()
self._validate_columns()
def _validate_columns(self):
"""Kiểm tra các cột bắt buộc"""
required_cols = ['timestamp', 'strike', 'expiry', 'option_type', 'premium']
missing = [col for col in required_cols if col not in self.df.columns]
if missing:
raise ValueError(f"Thiếu cột bắt buộc: {missing}")
def clean_and_normalize(self) -> pd.DataFrame:
"""
Làm sạch và chuẩn hóa dữ liệu
"""
# Chuyển đổi timestamp
self.df['timestamp'] = pd.to_datetime(self.df['timestamp'])
self.df['expiry'] = pd.to_datetime(self.df['expiry'])
# Tính thời gian đến hết hạn (ngày)
self.df['days_to_expiry'] = (self.df['expiry'] - self.df['timestamp']).dt.days
# Chuẩn hóa strike price
self.df['strike_normalized'] = self.df['strike'] / self.df.get('underlying_price', self.df['strike'])
# Xử lý giá trị null
self.df['premium'] = self.df['premium'].fillna(method='ffill')
# Loại bỏ outliers (giá trị premium âm hoặc quá cao)
q1 = self.df['premium'].quantile(0.01)
q99 = self.df['premium'].quantile(0.99)
self.df = self.df[(self.df['premium'] >= q1) & (self.df['premium'] <= q99)]
# Thêm cột moneyness
underlying = self.df['underlying_price'].iloc[0] if 'underlying_price' in self.df.columns else self.df['strike'].mean()
self.df['moneyness'] = np.where(
self.df['option_type'] == 'call',
self.df['strike'] / underlying,
underlying / self.df['strike']
)
return self.df
def calculate_greeks_proxy(self) -> pd.DataFrame:
"""
Tính toán các chỉ số Delta, Gamma, Vega proxy từ dữ liệu
(Lưu ý: Đây là ước tính đơn giản, cần mô hình Black-Scholes đầy đủ)
"""
# Delta proxy dựa trên moneyness
self.df['delta_proxy'] = np.where(
self.df['option_type'] == 'call',
np.clip((self.df['moneyness'] - 0.95) / 0.1, 0, 1),
np.clip((1.05 - self.df['moneyness']) / 0.1, 0, 1)
)
# Gamma proxy (cao nhất gần ATM)
self.df['gamma_proxy'] = np.exp(-abs(self.df['moneyness'] - 1) * 5) * 0.5
# Vega proxy (cao hơn cho expiry dài hơn)
self.df['vega_proxy'] = np.sqrt(self.df['days_to_expiry'] + 1) * 0.1
return self.df
def aggregate_by_expiry(self) -> pd.DataFrame:
"""
Tổng hợp dữ liệu theo ngày hết hạn
"""
agg_dict = {
'strike': ['min', 'max', 'count'],
'premium': ['mean', 'std'],
'delta_proxy': ['mean'],
'gamma_proxy': ['sum']
}
grouped = self.df.groupby(['expiry', 'option_type']).agg(agg_dict)
grouped.columns = ['_'.join(col).strip() for col in grouped.columns]
return grouped.reset_index()
class FundingRateProcessor:
"""
Xử lý dữ liệu tỷ lệ tài trợ (Funding Rates)
"""
def __init__(self, df: pd.DataFrame):
self.df = df.copy()
def clean(self) -> pd.DataFrame:
"""Làm sạch dữ liệu funding rate"""
self.df['timestamp'] = pd.to_datetime(self.df['timestamp'])
self.df = self.df.sort_values('timestamp')
# Chuyển đổi funding rate sang tỷ lệ phần trăm
if 'funding_rate' in self.df.columns:
self.df['funding_rate_pct'] = self.df['funding_rate'] * 100
# Xử lý giá trị bất thường
self.df['funding_rate_pct'] = self.df['funding_rate_pct'].clip(-1, 1)
return self.df
def calculate_funding_metrics(self) -> pd.DataFrame:
"""
Tính toán các chỉ số funding rate
"""
# Tỷ lệ tài trợ trung bình theo ngày
daily_funding = self.df.groupby(pd.Grouper(key='timestamp', freq='D'))['funding_rate_pct'].agg(['mean', 'std', 'min', 'max'])
# Tổng funding ước tính (annualized)
daily_funding['annualized_rate'] = daily_funding['mean'] * 365
# Funding volatility
daily_funding['funding_volatility'] = daily_funding['std'] * np.sqrt(365)
# Phát hiện funding spikes (> 0.5% một ngày)
daily_funding['is_spike'] = abs(daily_funding['mean']) > 0.5
return daily_funding
def detect_funding_regimes(self, window: int = 7) -> pd.DataFrame:
"""
Phát hiện các chế độ thị trường dựa trên funding rate
"""
self.df['funding_ma'] = self.df['funding_rate_pct'].rolling(window=window).mean()
self.df['funding_std'] = self.df['funding_rate_pct'].rolling(window=window).std()
# Phân loại regime
def classify_regime(row):
if row['funding_ma'] > 0.1:
return 'bullish_high_funding'
elif row['funding_ma'] < -0.1:
return 'bearish_high_funding'
elif row['funding_std'] > 0.3:
return 'volatile'
else:
return 'neutral'
self.df['regime'] = self.df.apply(classify_regime, axis=1)
return self.df
Phân tích dữ liệu Quyền chọn (Options Chain Analysis)
4.1 Phân tích Open Interest và Volume
Open Interest (OI) là chỉ số quan trọng cho thấy dòng tiền và tâm lý thị trường:# File: scripts/analyze_options.py
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from utils.data_loader import OptionsDataProcessor
Cài đặt style
plt.style.use('seaborn-v0_8-whitegrid')
sns.set_palette("husl")
class OptionsAnalyzer:
"""
Phân tích chuyên sâu dữ liệu quyền chọn
"""
def __init__(self, df: pd.DataFrame):
self.df = df
self.processor = OptionsDataProcessor(df)
self.cleaned_df = self.processor.clean_and_normalize()
self.greeks_df = self.processor.calculate_greeks_proxy()
def calculate_max_pain(self) -> float:
"""
Tính Max Pain - mức giá gây thiệt hại tối đa cho người nắm giữ quyền chọn
"""
# Nhóm theo strike price
strike_oi = self.cleaned_df.groupby('strike').agg({
'premium': 'count', # Số lượng hợp đồng
'option_type': 'first'
}).reset_index()
# Tính pain cho mỗi mức giá tiềm năng
pain_by_strike = []
for test_price in strike_oi['strike'].unique():
call_pain = 0
put_pain = 0
for _, row in self.cleaned_df[self.cleaned_df['strike'] == test_price].iterrows():
if row['option_type'] == 'call':
call_pain += max(0, test_price - row['strike'])
else:
put_pain += max(0, row['strike'] - test_price)
pain_by_strike.append({
'strike': test_price,
'total_pain': call_pain + put_pain
})
pain_df = pd.DataFrame(pain_by_strike)
max_pain_strike = pain_df.loc[pain_df['total_pain'].idxmin(), 'strike']
return max_pain_strike
def analyze_oi_distribution(self) -> dict:
"""
Phân tích phân bố Open Interest theo strike price
"""
# OI cho call options
call_oi = self.greeks_df[self.greeks_df['option_type'] == 'call'].groupby('strike').agg({
'strike_count': 'sum',
'delta_proxy_mean': 'mean'
})
# OI cho put options
put_oi = self.greeks_df[self.greeks_df['option_type'] == 'put'].groupby('strike').agg({
'strike_count': 'sum',
'delta_proxy_mean': 'mean'
})
# Tính Put/Call ratio
total_call_oi = call_oi['strike_count'].sum()
total_put_oi = put_oi['strike_count'].sum()
pc_ratio = total_put_oi / total_call_oi if total_call_oi > 0 else 0
return {
'call_oi': call_oi,
'put_oi': put_oi,
'pc_ratio': pc_ratio,
'total_oi': total_call_oi + total_put_oi,
'call_dominant_levels': call_oi.nlargest(5, 'strike_count').index.tolist(),
'put_dominant_levels': put_oi.nlargest(5, 'strike_count').index.tolist()
}
def identify_gamma_exposure(self) -> pd.DataFrame:
"""
Xác định các mức gamma exposure - vùng có thể gây biến động mạnh
"""
# Gamma = Delta thay đổi khi giá thay đổi
# ATM options có gamma cao nhất
self.greeks_df['gamma_exposure'] = (
self.greeks_df['gamma_proxy'] *
self.greeks_df['strike_count'] if 'strike_count' in self.greeks_df.columns else 0
)
# Sắp xếp theo gamma exposure
gamma_levels = self.greeks_df.nlargest(10, 'gamma_proxy')[
['strike', 'option_type', 'gamma_proxy', 'delta_proxy']
]
return gamma_levels
def generate_options_report(self) -> dict:
"""
Tạo báo cáo tổng hợp về thị trường quyền chọn
"""
oi_analysis = self.analyze_oi_distribution()
max_pain = self.calculate_max_pain()
gamma_levels = self.identify_gamma_exposure()
report = {
'summary': {
'total_contracts': len(self.cleaned_df),
'date_range': f"{self.cleaned_df['timestamp'].min()} to {self.cleaned_df['timestamp'].max()}",
'put_call_ratio': round(oi_analysis['pc_ratio'], 3),
'max_pain_strike': max_pain,
'atm_options_count': len(self.greeks_df[(self.greeks_df['moneyness'] > 0.98) &
(self.greeks_df['moneyness'] < 1.02)])
},
'oi_analysis': oi_analysis,
'gamma_levels': gamma_levels.to_dict('records'),
'recommendations': self._generate_recommendations(oi_analysis, max_pain)
}
return report
def _generate_recommendations(self, oi_analysis: dict, max_pain: float) -> list:
"""
Đưa ra khuyến nghị dựa trên phân tích
"""
recommendations = []
# Phân tích Put/Call ratio
if oi_analysis['pc_ratio'] > 1.5:
recommendations.append({
'type': 'bearish_sentiment',
'message': f"PCR = {oi_analysis['pc_ratio']:.2f} cho thấy tâm lý Bearish, "
f"nhiều nhà đầu tư tìm kiếm bảo vệ bên dưới",
'action': 'Cân nhắc các chiến lược hedged'
})
elif oi_analysis['pc_ratio'] < 0.6:
recommendations.append({
'type': 'bullish_sentiment',
'message': f"PCR = {oi_analysis['pc_ratio']:.2f} cho thấy tâm lý Bullish mạnh",
'action': 'Xem xét momentum strategies'
})
# Max Pain
recommendations.append({
'type': 'max_pain',
'message': f"Max Pain ở mức ${max_pain:,.0f}",
'action': 'Thị trường có xu hướng bị kéo về mức này khi expiry gần'
})
return recommendations
Ví dụ sử dụng
if __name__ == "__main__":
# Load dữ liệu (giả định đã tải từ Tardis)
df = pd.read_csv('data/raw/options_deribit_BTC_2024.csv')
analyzer = OptionsAnalyzer(df)
report = analyzer.generate_options_report()
print("=" * 60)
print("BÁO CÁO PHÂN TÍCH QUYỀN CHỌN BTC")
print("=" * 60)
print(f"Tổng hợp đồng: {report['summary']['total_contracts']}")
print(f"PCR: {report['summary']['put_call_ratio']}")
print(f"Max Pain: ${report['summary']['max_pain_strike']:,.0f}")
print("\nKhuyến nghị:")
for rec in report['recommendations']:
print(f" • [{rec['type']}] {rec['message']}")
print(f" → {rec['action']}")
Phân tích Funding Rates và Correlation
5.1 Chiến lược giao dịch dựa trên Funding Rate
Funding Rate là chỉ số quan trọng để đánh giá tâm lý thị trường và xác định cơ hội arbitrage:# File: scripts/analyze_funding.py
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
from utils.data_loader import FundingRateProcessor
class FundingRateStrategyAnalyzer:
"""
Phân tích chiến lược giao dịch dựa trên Funding Rates
"""
def __init__(self, df: pd.DataFrame, price_df: pd.DataFrame = None):
self.df = df
self.processor = FundingRateProcessor(df)
self.cleaned_df = self.processor.clean()
self.price_df = price_df
def calculate_historical_funding_stats(self) -> dict:
"""
Tính toán thống kê funding rate lịch sử
"""
stats_dict = {
'mean': self.cleaned_df['funding_rate_pct'].mean(),
'median': self.cleaned_df['funding_rate_pct'].median(),
'std': self.cleaned_df['funding_rate_pct'].std(),
'min': self.cleaned_df['funding_rate_pct'].min(),
'max': self.cleaned_df['funding_rate_pct'].max(),
'p5': self.cleaned_df['funding_rate_pct'].quantile(0.05),
'p95': self.cleaned_df['funding_rate_pct'].quantile(0.95),
'annualized_avg': self.cleaned_df['funding_rate_pct'].mean() * 365
}
return stats_dict
def detect_funding_regimes(self) -> pd.DataFrame:
"""
Phát hiện các chế độ funding dựa trên thresholds
"""
df = self.cleaned_df.copy()
# Định nghĩa regimes
conditions = [
(df['funding_rate_pct'] > 0.1), # High positive funding
(df['funding_rate_pct'] < -0.1), # High negative funding
(df['funding_rate_pct'].abs() < 0.02) # Near zero
]
choices = ['high_positive', 'high_negative', 'neutral']
df['regime'] = np.select(conditions, choices, default='moderate')
# Thống kê theo regime
regime_stats = df.groupby('regime').agg({
'funding_rate_pct': ['count', 'mean', 'std'],
'timestamp': ['min', 'max']
})
return df, regime_stats
def calculate_funding_price_correlation(self) -> dict:
"""
Tính correlation giữa funding rate và giá
"""
if self.price_df is None:
return {'error': 'Cần cung cấp price_df để tính correlation'}
# Merge data
merged = pd.merge_asof(
self.cleaned_df.sort_values('timestamp'),
self.price_df.sort_values('timestamp'),
on='timestamp',
direction='nearest',
tolerance=pd.Timedelta('1h')
)
# Tính returns
merged['price_return'] = merged['close'].pct_change()
merged['funding_change'] = merged['funding_rate_pct'].diff()
# Correlation
valid_data = merged.dropna()
correlation = valid_data['funding_rate_pct'].corr(valid_data['price_return'])
# Granger causality test (đơn giản hóa)
lag_corr = {}
for lag in [1, 2, 3, 8, 24]:
lag_corr[f'lag_{lag}'] = valid_data['funding_rate_pct'].corr(
valid_data['price_return'].shift(lag)
)
return {
'correlation': correlation,
'lag_correlations': lag_corr,
'interpretation': self._interpret_correlation(correlation)
}
def _interpret_correlation(self, corr: float) -> str:
"""Diễn giải hệ số tương quan"""
abs_corr = abs(corr)
if abs_corr < 0.1:
return "Gần như không có tương quan"
elif abs_corr < 0.3:
return "Tương quan yếu"
elif abs_corr < 0.5:
return "Tương quan trung bình"
elif abs_corr < 0.7:
return "Tương quan khá mạnh"
else:
return "Tương quan mạnh"
def backtest_funding_strategy(self, threshold: float = 0.05,
holding_period: int = 24) -> pd.DataFrame:
"""
Backtest chiến lược giao dịch dựa trên funding threshold
"""
df = self.cleaned_df.copy()
df = df.sort_values('timestamp')
# Tính signal
df['signal'] = 0
df.loc[df['funding_rate_pct'] > threshold, 'signal'] = -1 # Short when high funding
df.loc[df['funding_rate_pct'] < -threshold, 'signal'] = 1 # Long when negative funding
# Tính returns (giả định position size = 1)
if self.price_df is not None:
merged = pd.merge_asof(
df,
self.price_df.sort_values('timestamp'),
on='timestamp',
direction='nearest'
)
merged['strategy_return'] = merged['signal'].shift(1) * merged['close'].pct_change(holding_period)
# Loại bỏ NaN
valid_returns = merged.dropna()
results = {
'total_trades': (valid_returns['signal'] != 0).sum(),
'winning_trades': (valid_returns['strategy_return'] > 0).sum(),
'total_return': valid_returns['strategy_return'].sum(),
'avg_return': valid_returns['strategy_return'].mean(),
'sharpe_ratio': valid_returns['strategy_return'].mean() / valid_returns['strategy_return'].std() * np.sqrt(365) if valid_returns['strategy_return'].std() > 0 else 0,
'max_drawdown': self._calculate_max_drawdown(valid_returns['strategy_return'].cumsum())
}
return pd.DataFrame([results])
return pd.DataFrame([{'error': 'Không có dữ liệu giá để backtest'}])
def _calculate_max_drawdown(self, cumulative_returns: pd.Series) -> float:
"""Tính maximum drawdown"""
running_max = cumulative_returns.expanding().max()
drawdown = cumulative_returns - running_max