Mở đầu: Khi Độ biến động Nói lên Câu Chuyện Của Thị Trường
Tôi còn nhớ rõ ngày hôm đó - tháng 3 năm 2024, khi Bitcoin bất ngờ tăng 15% chỉ trong 4 giờ. Trong khi các trader amateur đang hân hoan vì lợi nhuận, tôi - lúc đó đang vận hành một quỹ tự quản - nhận ra rằng đây là thời điểm vàng để kiểm tra lại mô hình dự đoán biến động của mình. Tôi đã dành 3 ngày liên tục xây dựng công cụ phân tích volatility smile từ OKX options chain. Kết quả? Tôi đã bắt được đáy của đợt squeeze tiếp theo với độ chính xác khiến nhiều đồng nghiệp phải ngạc nhiên. Bài viết hôm nay sẽ chia sẻ toàn bộ quy trình, từ việc lấy dữ liệu options chain thô từ OKX cho đến việc xây dựng volatility smile - công cụ không thể thiếu của bất kỳ options trader chuyên nghiệp nào.Volatility Smile Là Gì và Tại Sao Nó Quan Trọng?
Volatility smile (nụ cười biến động) là đồ thị thể hiện mối quan hệ giữa implied volatility (IV) và strike price của các options cùng ngày đáo hạn. Đặc điểm kinh điển của nó là:
- Đuôi dưới (OTM puts): IV cao hơn - phản ánh nỗi sợ hãi về crash
- ATM options: IV thấp nhất - nơi cung cầu cân bằng
- Đuôi trên (OTM calls): IV cao hơn - do demand cho các chiến lược upside hedge
Trong thị trường crypto, volatility smile càng quan trọng hơn vì:
- Thị trường hoạt động 24/7 với thanh khoản biến đổi liên tục
- Các sự kiện vĩ mô (halving, quy định) tạo ra skew mạnh
- Depeg events và liquidation cascades tạo fat tails đặc trưng
Lấy Dữ Liệu OKX Options Chain qua HolySheep AI
Điều đầu tiên bạn cần là dữ liệu options chain thực tế. Thay vì phải tự viết scraper phức tạp, bạn có thể sử dụng HolySheep AI để phân tích và xử lý dữ liệu này với chi phí cực thấp - chỉ từ $0.42/MTok với DeepSeek V3.2, tiết kiệm đến 85% so với các provider khác.
import requests
import json
import pandas as pd
from datetime import datetime
Cấu hình HolySheep AI
HOLYSHEEP_API_KEY = "YOUR_HOLYSHEEP_API_KEY"
HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1"
def query_okx_options_analysis(prompt: str) -> str:
"""
Gọi HolySheep AI để phân tích dữ liệu options
Chi phí: ~$0.42/MTok với DeepSeek V3.2 (tiết kiệm 85%+)
Độ trễ: <50ms
"""
headers = {
"Authorization": f"Bearer {HOLYSHEEP_API_KEY}",
"Content-Type": "application/json"
}
payload = {
"model": "deepseek-v3.2",
"messages": [
{
"role": "system",
"content": "Bạn là chuyên gia phân tích options và volatility. Hãy phân tích dữ liệu OKX options chain một cách chi tiết."
},
{
"role": "user",
"content": prompt
}
],
"temperature": 0.3,
"max_tokens": 2000
}
response = requests.post(
f"{HOLYSHEEP_BASE_URL}/chat/completions",
headers=headers,
json=payload
)
if response.status_code == 200:
return response.json()["choices"][0]["message"]["content"]
else:
raise Exception(f"API Error: {response.status_code} - {response.text}")
Ví dụ: Phân tích volatility smile hiện tại
analysis_prompt = """
Phân tích cấu trúc volatility smile cho BTC options trên OKX:
- Underlying: BTC
- Expiration: 2024-03-29
- Current spot: $68,500
- strikes: [60000, 62000, 64000, 66000, 68000, 70000, 72000, 74000]
- IVs: [72%, 68%, 62%, 58%, 60%, 65%, 70%, 75%]
Hãy nhận định:
1. Skew direction và mức độ
2. Cơ hội arbitrage
3. Khuyến nghị chiến lược
"""
result = query_okx_options_analysis(analysis_prompt)
print("=== PHÂN TÍCH VOLATILITY SMILE ===")
print(result)
Xây dựng Volatility Smile từ Dữ liệu Thực
Bây giờ, hãy xây dựng một hệ thống hoàn chỉnh để lấy dữ liệu từ OKX API và tính toán implied volatility cho mỗi strike price.
import requests
import numpy as np
from scipy.stats import norm
from scipy.optimize import brentq
import pandas as pd
from typing import Dict, List, Tuple
from datetime import datetime, timedelta
import json
import hashlib
class OKXOptionsDataFetcher:
"""Lấy dữ liệu options chain từ OKX Exchange"""
def __init__(self, instrument_id: str = "BTC-USD"):
self.base_url = "https://www.okx.com"
self.instrument_id = instrument_id
self.spot_price = self._get_spot_price()
def _get_spot_price(self) -> float:
"""Lấy giá spot hiện tại của underlying"""
endpoint = f"{self.base_url}/api/v5/market/ticker"
params = {"instId": f"{self.instrument_id}-USD"}
response = requests.get(endpoint, params=params)
data = response.json()
return float(data['data'][0]['last'])
def get_options_chain(self, expiry: str) -> List[Dict]:
"""
Lấy danh sách tất cả options cho một expiry cụ thể
expiry format: "20240329"
"""
endpoint = f"{self.base_url}/api/v5/market/opt/strike-price"
params = {
"instId": f"{self.instrument_id}-USD",
"exp": expiry,
"limit": "100"
}
response = requests.get(endpoint, params=params)
data = response.json()
options = []
for strike_info in data['data']:
strike = float(strike_info['strike'])
opt_type = strike_info['optType'] # 'C' hoặc 'C'
# Lấy thông tin chi tiết cho từng option
inst_id = f"{self.instrument_id}-USD-{expiry}-{opt_type}-{int(strike)}"
opt_data = self._get_option_details(inst_id)
if opt_data:
options.append({
'strike': strike,
'type': 'call' if opt_type == 'C' else 'put',
'bid': float(opt_data.get('bidPx', 0)),
'ask': float(opt_data.get('askPx', 0)),
'iv_bid': float(opt_data.get('bidIv', 0)),
'iv_ask': float(opt_data.get('askIv', 0)),
'volume': float(opt_data.get('vol24h', 0)),
'open_interest': float(opt_data.get('oi', 0))
})
return options
def _get_option_details(self, inst_id: str) -> Dict:
"""Lấy chi tiết một option cụ thể"""
endpoint = f"{self.base_url}/api/v5/market/ticker"
params = {"instId": inst_id}
try:
response = requests.get(endpoint, params=params, timeout=5)
return response.json()['data'][0]
except:
return {}
class BlackScholes:
"""Tính toán Black-Scholes và Implied Volatility"""
@staticmethod
def d1(S: float, K: float, T: float, r: float, sigma: float) -> float:
return (np.log(S/K) + (r + sigma**2/2)*T) / (sigma * np.sqrt(T))
@staticmethod
def d2(S: float, K: float, T: float, r: float, sigma: float) -> float:
return BlackScholes.d1(S, K, T, r, sigma) - sigma * np.sqrt(T)
@staticmethod
def call_price(S: float, K: float, T: float, r: float, sigma: float) -> float:
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: float, K: float, T: float, r: float, sigma: float) -> float:
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(price: float, S: float, K: float, T: float,
r: float, option_type: str = 'call') -> float:
"""
Tính IV bằng phương pháp Brent
"""
def objective(sigma):
if option_type == 'call':
return BlackScholes.call_price(S, K, T, r, sigma) - price
else:
return BlackScholes.put_price(S, K, T, r, sigma) - price
try:
iv = brentq(objective, 0.001, 5.0) # Tìm IV trong khoảng 0.1% - 500%
return iv
except:
return np.nan
class VolatilitySmileBuilder:
"""Xây dựng và phân tích volatility smile"""
def __init__(self, options_data: List[Dict], spot_price: float,
time_to_expiry: float, risk_free_rate: float = 0.05):
self.options_data = options_data
self.spot_price = spot_price
self.T = time_to_expiry
self.r = risk_free_rate
self.bs = BlackScholes()
def calculate_iv_for_all_strikes(self) -> pd.DataFrame:
"""Tính IV cho tất cả các strikes"""
results = []
for opt in self.options_data:
# Tính IV từ giá bid-ask trung bình
mid_price = (opt['bid'] + opt['ask']) / 2
# Nếu có sẵn IV từ exchange, sử dụng luôn
if opt.get('iv_bid') and opt.get('iv_ask'):
iv = (opt['iv_bid'] + opt['iv_ask']) / 2 / 100 # Convert từ %
else:
# Tính IV từ giá
iv = self.bs.implied_volatility(
mid_price, self.spot_price, opt['strike'],
self.T, self.r, opt['type']
)
# Tính moneyness
moneyness = opt['strike'] / self.spot_price
results.append({
'strike': opt['strike'],
'type': opt['type'],
'bid': opt['bid'],
'ask': opt['ask'],
'mid_price': mid_price,
'iv': iv * 100 if not np.isnan(iv) else None,
'moneyness': moneyness,
'log_moneyness': np.log(moneyness),
'volume': opt['volume'],
'open_interest': opt['open_interest']
})
return pd.DataFrame(results)
def fit_polynomial_smile(self, df: pd.DataFrame, degree: int = 2) -> np.poly1d:
"""Fit polynomial cho volatility smile"""
# Chỉ sử dụng OTM options (puts cho strikes thấp, calls cho strikes cao)
otm_puts = df[(df['type'] == 'put') & (df['strike'] < self.spot_price)]
otm_calls = df[(df['type'] == 'call') & (df['strike'] >= self.spot_price)]
# Kết hợp dữ liệu
combined = pd.concat([otm_puts, otm_calls])
# Loại bỏ NaN values
combined = combined.dropna(subset=['iv', 'log_moneyness'])
# Fit polynomial
return np.polyfit(combined['log_moneyness'], combined['iv'], degree)
def extract_vol_skew(self, df: pd.DataFrame) -> Dict:
"""Trích xuất các thông số skew quan trọng"""
puts = df[df['type'] == 'put'].copy()
calls = df[df['type'] == 'call'].copy()
# 25-delta put IV
atm_iv = df[abs(df['moneyness'] - 1) < 0.05]['iv'].mean()
# 25-delta risk reversal (RR)
rr = None
if len(puts) >= 2 and len(calls) >= 2:
low_strike_put = puts[puts['strike'] < self.spot_price].iloc[-1]
high_strike_call = calls[calls['strike'] > self.spot_price].iloc[0]
rr = high_strike_call['iv'] - low_strike_put['iv']
# 25-delta butterfly
bf = None
if atm_iv:
wing_iv = (low_strike_put['iv'] + high_strike_call['iv']) / 2
bf = wing_iv - atm_iv
return {
'atm_vol': atm_iv,
'risk_reversal_25d': rr,
'butterfly_25d': bf,
'smile_skew': abs(rr) if rr else 0
}
==================== SỬ DỤNG THỰC TẾ ====================
def main():
print("=== VOLATILITY SMILE BUILDER ===")
print(f"Thời gian: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
# Khởi tạo fetcher
fetcher = OKXOptionsDataFetcher(instrument_id="BTC")
# Lấy expiry tiếp theo (28 ngày)
expiry = (datetime.now() + timedelta(days=28)).strftime("%Y%m%d")
# Lấy dữ liệu options
print(f"Đang lấy options chain cho expiry: {expiry}")
options = fetcher.get_options_chain(expiry)
# Tính T (năm)
T = 28 / 365
# Xây dựng volatility smile
builder = VolatilitySmileBuilder(
options_data=options,
spot_price=fetcher.spot_price,
time_to_expiry=T,
risk_free_rate=0.05
)
# Tính IV cho tất cả strikes
df = builder.calculate_iv_for_all_strikes()
print("\n=== VOLATILITY SMILE DATA ===")
print(df.to_string())
# Trích xuất skew metrics
skew_metrics = builder.extract_vol_skew(df)
print("\n=== SKEW METRICS ===")
for key, value in skew_metrics.items():
if value is not None:
print(f"{key}: {value:.2f}%")
# Fit polynomial smile
poly_coeffs = builder.fit_polynomial_smile(df, degree=2)
print(f"\n=== POLYNOMIAL COEFFICIENTS ===")
print(f"y = {poly_coeffs[0]:.4f}x² + {poly_coeffs[1]:.4f}x + {poly_coeffs[2]:.4f}")
return df, skew_metrics
if __name__ == "__main__":
df, metrics = main()
Trực quan hóa Volatility Smile
Để hiểu rõ hơn về cấu trúc biến động, hãy tạo các biểu đồ trực quan với matplotlib.
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.gridspec import GridSpec
import seaborn as sns
class VolatilitySmileVisualizer:
"""Trực quan hóa Volatility Smile"""
def __init__(self, df: pd.DataFrame, spot_price: float):
self.df = df
self.spot_price = spot_price
self._setup_style()
def _setup_style(self):
"""Cấu hình style cho biểu đồ"""
plt.style.use('seaborn-v0_8-darkgrid')
self.fig = plt.figure(figsize=(16, 12))
self.gs = GridSpec(3, 2, figure=self.fig, hspace=0.3, wspace=0.25)
def plot_smile_3d(self):
"""Biểu đồ 3D của volatility surface"""
ax = self.fig.add_subplot(self.gs[0, :], projection='3d')
# Chuẩn bị dữ liệu
strikes = self.df['strike'].values
ivs = self.df['iv'].values
moneyness = self.df['moneyness'].values
# Scatter plot
scatter = ax.scatter(strikes, moneyness, ivs,
c=self.df['type'].map({'call': 'blue', 'put': 'red'}),
s=100, alpha=0.7)
ax.set_xlabel('Strike Price')
ax.set_ylabel('Moneyness (K/S)')
ax.set_zlabel('Implied Volatility (%)')
ax.set_title('Volatility Smile - 3D View')
return ax
def plot_smile_2d(self):
"""Biểu đồ 2D cổ điển của volatility smile"""
ax = self.fig.add_subplot(self.gs[1, 0])
# Phân tách calls và puts
puts = self.df[self.df['type'] == 'put']
calls = self.df[self.df['type'] == 'call']
# Vẽ đường cong
ax.plot(puts['strike'], puts['iv'], 'ro-', label='Puts', markersize=8)
ax.plot(calls['strike'], calls['iv'], 'bo-', label='Calls', markersize=8)
# Đánh dấu ATM
atm_strike = self.spot_price
ax.axvline(x=atm_strike, color='green', linestyle='--', alpha=0.5, label='ATM')
ax.axhline(y=puts[abs(puts['strike'] - atm_strike) < 1000]['iv'].mean(),
color='gray', linestyle=':', alpha=0.5)
ax.set_xlabel('Strike Price (USD)')
ax.set_ylabel('Implied Volatility (%)')
ax.set_title('Volatility Smile - 2D View')
ax.legend()
ax.grid(True, alpha=0.3)
return ax
def plot_skew_metrics(self, metrics: Dict):
"""Biểu đồ các chỉ số skew"""
ax = self.fig.add_subplot(self.gs[1, 1])
metrics_names = list(metrics.keys())
metrics_values = list(metrics.values())
colors = ['#3498db' if v >= 0 else '#e74c3c' for v in metrics_values]
bars = ax.barh(metrics_names, metrics_values, color=colors, alpha=0.7)
ax.axvline(x=0, color='black', linestyle='-', linewidth=0.5)
ax.set_xlabel('Volatility (%)')
ax.set_title('Skew Metrics')
# Thêm giá trị trên thanh
for bar, val in zip(bars, metrics_values):
if val is not None:
ax.text(val + 0.5 if val >= 0 else val - 0.5,
bar.get_y() + bar.get_height()/2,
f'{val:.1f}%', va='center', fontsize=10)
return ax
def plot_term_structure(self, time_strikes: List[Dict]):
"""Biểu đồ term structure của ATM volatility"""
ax = self.fig.add_subplot(self.gs[2, :])
# Giả sử có dữ liệu cho nhiều expiry
expiries = [d['expiry'] for d in time_strikes]
atm_vols = [d['atm_vol'] for d in time_strikes]
ax.plot(expiries, atm_vols, 'go-', markersize=10, linewidth=2, label='ATM Vol')
ax.fill_between(expiries, atm_vols, alpha=0.3)
ax.set_xlabel('Expiry Date')
ax.set_ylabel('ATM Implied Volatility (%)')
ax.set_title('ATM Volatility Term Structure')
ax.grid(True, alpha=0.3)
# Format trục x
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
plt.setp(ax.xaxis.get_majorticklabels(), rotation=45)
return ax
def save_and_show(self, filename: str = 'volatility_smile.png'):
"""Lưu và hiển thị biểu đồ"""
plt.tight_layout()
plt.savefig(filename, dpi=150, bbox_inches='tight')
print(f"Biểu đồ đã lưu: {filename}")
plt.show()
def create_volatility_report(df: pd.DataFrame, metrics: Dict,
spot_price: float) -> str:
"""
Tạo báo cáo phân tích volatility smile
Gửi kết quả đến HolySheep AI để phân tích chuyên sâu
"""
# Chuẩn bị dữ liệu tổng hợp
report_data = {
'spot_price': spot_price,
'total_options': len(df),
'strike_range': f"{df['strike'].min():.0f} - {df['strike'].max():.0f}",
'iv_range': f"{df['iv'].min():.1f}% - {df['iv'].max():.1f}%",
'atm_iv': metrics.get('atm_vol', 0),
'risk_reversal': metrics.get('risk_reversal_25d', 0),
'butterfly': metrics.get('butterfly_25d', 0)
}
report_prompt = f"""
PHÂN TÍCH VOLATILITY SMILE - BÁO CÁO TỰ ĐỘNG
=== THÔNG TIN THỊ TRƯỜNG ===
- Spot Price: ${report_data['spot_price']:,.0f}
- Số lượng Options: {report_data['total_options']}
- Strike Range: {report_data['strike_range']}
- IV Range: {report_data['iv_range']}
=== SKEW METRICS ===
- ATM IV: {report_data['atm_iv']:.2f}%
- 25-Delta Risk Reversal: {report_data['risk_reversal']:.2f}%
- 25-Delta Butterfly: {report_data['butterfly']:.2f}%
=== DỮ LIỆU CHI TIẾT (TOP 10) ===
{df.nlargest(10, 'open_interest')[['strike', 'type', 'iv', 'volume', 'open_interest']].to_string()}
Hãy phân tích:
1. Đặc điểm của volatility smile hiện tại
2. So sánh với các đợt volatility squeeze trước đó
3. Cơ hội trading và risks
4. Khuyến nghị chiến lược options cụ thể
"""
return report_prompt
==================== CHẠY TRỰC QUAN HÓA ====================
if __name__ == "__main__":
# Giả lập dữ liệu cho demo
np.random.seed(42)
spot = 68500
demo_data = []
for k in np.linspace(55000, 80000, 26):
iv_base = 60 - 0.5 * ((k - spot)/spot)**2 * 100 # Parabola shape
iv_put = iv_base + 5 * (spot/k - 1) * 100 + np.random.normal(0, 2)
iv_call = iv_base - 5 * (k/spot - 1) * 100 + np.random.normal(0, 2)
demo_data.append({
'strike': k, 'type': 'put', 'iv': max(30, iv_put),
'bid': 100, 'ask': 102, 'volume': np.random.randint(100, 1000),
'open_interest': np.random.randint(500, 5000)
})
demo_data.append({
'strike': k, 'type': 'call', 'iv': max(30, iv_call),
'bid': 100, 'ask': 102, 'volume': np.random.randint(100, 1000),
'open_interest': np.random.randint(500, 5000)
})
df = pd.DataFrame(demo_data)
df['moneyness'] = df['strike'] / spot
df['log_moneyness'] = np.log(df['moneyness'])
metrics = {
'atm_vol': df[abs(df['moneyness'] - 1) < 0.05]['iv'].mean(),
'risk_reversal_25d': 3.5,
'butterfly_25d': -1.2,
'smile_skew': 3.5
}
# Tạo visualizer
viz = VolatilitySmileVisualizer(df, spot)
viz.plot_smile_2d()
viz.plot_skew_metrics(metrics)
viz.save_and_show('volatility_smile_demo.png')
# Tạo báo cáo gửi đến AI
report = create_volatility_report(df, metrics, spot)
print("\n=== REPORT PROMPT FOR AI ===")
print(report)
Chiến lược Trading dựa trên Volatility Smile
Sau khi có volatility smile, bạn có thể áp dụng nhiều chiến lược options. Dưới đây là một số chiến lược phổ biến được tích hợp với AI để phân tích tự động.
import numpy as np
from dataclasses import dataclass
from typing import Optional, List
from enum import Enum
class StrategyType(Enum):
RISK_REVERSAL = "risk_reversal"
COLLAR = "collar"
IRON_CONDOR = "iron_condor"
STRADDLE = "straddle"
BUTTERFLY = "butterfly"
Calendar = "calendar"
@dataclass
class Option:
strike: float
premium: float
option_type: str # 'call' or 'put'
expiry: str
@dataclass
class Strategy:
name: str
legs: List[Option]
max_profit: Optional[float]
max_loss: Optional[float]
breakeven: List[float]
class OptionsStrategyAnalyzer:
"""Phân tích và đề xuất chiến lược options dựa trên volatility smile"""
def __init__(self, spot_price: float, iv_surface: dict,
risk_free_rate: float = 0.05):
self.spot = spot_price
self.iv = iv_surface
self.r = risk_free_rate
def analyze_risk_reversal(self, tenor_days: int = 30) -> Strategy:
"""
Chiến lược Risk Reversal:
- Bán OTM put (được premium)
- Mua OTM call (trả premium)
- Thường used khi expect upside nhưng muốn hedge downside
"""
# Tìm strikes phù hợp
otm_put_strike = self.spot * 0.95 # 5% OTM
otm_call_strike = self.spot * 1.05 # 5% OTM
T = tenor_days / 365
# Ước tính premium sử dụng BS
put_premium = self._estimate_premium(otm_put_strike, T, 'put')
call_premium = self._estimate_premium(otm_call_strike, T, 'call')
net_credit = put_premium - call_premium
# Max profit: unlimited upside minus net credit
# Max loss: (otm_put_strike - spot) + net_credit
max_loss = (self.spot - otm_put_strike) - net_credit
breakeven = self.spot - max_loss
return Strategy(
name="Risk Reversal",
legs=[
Option(otm_put_strike, put_premium, 'put', f"{tenor_days}d"),
Option(otm_call_strike, call_premium, 'call', f"{tenor_days}d")
],
max_profit=None, # Unlimited
max_loss=max_loss,
breakeven=[breakeven]
)
def analyze_iron_condor(self, tenor_days: int = 30) -> Strategy:
"""
Chiến lược Iron Condor:
- Bán OTM call spread
- Bán OTM put spread
- Profit khi price stay within range
"""
wings = 0.05 # 5% wings
body = 0.025 # 2.5% body width
put_sell = self.spot * (1 - body)
put_buy = self.spot * (1 - wings)
call_sell = self.spot * (1 + body)
call_buy = self.spot * (1 + wings)
T = tenor_days / 365
premiums = {
'put_sell': self._estimate_premium(put_sell, T, 'put'),
'put_buy': self._estimate_premium(put_buy, T, 'put'),
'call_sell': self._estimate_premium(call_sell, T, 'call'),
'call_buy': self._estimate_premium(call_buy, T, 'call')
}
net_credit = (premiums['put_sell'] - premiums['put_buy'] +
premiums['call_sell'] - premiums['call_buy'])
max_profit = net_credit
max_loss = (wings - body) * self.spot - net_credit
breakeven = [self.spot - body * self.spot - net_credit,
self.spot + body * self.spot + net