สวัสดีครับ วันนี้ผมจะมาแชร์ประสบการณ์ตรงในการดึงข้อมูล BTC และ ETH Options จาก Deribit เพื่อใช้ทำ Quantitative Backtest ครับ เรื่องนี้อาจฟังดูเทคนิคมาก แต่ผมจะพยายามอธิบายให้เข้าใจง่ายที่สุด
ปัญหาจริงที่ผมเจอ: ConnectionError และ 401 Unauthorized
ตอนเริ่มทำโปรเจกต์นี้ ผมเจอปัญหาหลายอย่างมาก:
- ปัญหาแรก: พยายามใช้ free API ของ Deribit โดยตรง แต่ rate limit ต่ำมาก ดึงได้แค่ 10 requests/วินาที ข้อมูล historical options ดูไม่ครบ
- ปัญหาที่สอง: ได้ข้อมูลมาแล้วแต่ format ซับซ้อน ต้องมา parse เองเยอะมาก
- ปัญหาที่สาม: ลองใช้บริการอื่น แต่ latency สูงเกินไป (300-500ms) ทำให้ backtest ช้า
- ปัญหาที่สี่: Export CSV จาก Deribit มีปัญหาเรื่อง timezone และ timestamp format
จนกระทั่งได้ลองใช้ Tardis API ผ่าน HolySheep AI ปัญหาทั้งหมดแก้ได้ในคราวเดียว
Tardis API คืออะไร และทำไมต้องใช้?
Tardis เป็นบริการที่ aggregate ข้อมูล cryptocurrency exchanges รวมถึง Deribit โดยเฉพาะ ข้อดีหลักๆ คือ:
- Historical data ครบถ้วน ทั้ง options, futures, spot
- Format มาตรฐาน ง่ายต่อการ parse
- มี CSV export สำเร็จรูป
- WebSocket support สำหรับ real-time data
# ตัวอย่างการเชื่อมต่อ Tardis API
import requests
import pandas as pd
from datetime import datetime, timedelta
การตั้งค่า Tardis credentials
TARDIS_API_KEY = "your_tardis_api_key"
TARDIS_API_SECRET = "your_tardis_api_secret"
Base URL สำหรับ Deribit options historical data
BASE_URL = "https://api.tardis-dev.com/v1"
def get_deribit_options_candles(
symbol: str = "BTC-28MAR25-95000-C", # BTC options format
start_date: str = "2025-01-01",
end_date: str = "2025-03-28",
timeframe: str = "1h"
):
"""
ดึงข้อมูล OHLCV ของ Deribit options
Args:
symbol: Options symbol (เช่น BTC-28MAR25-95000-C)
start_date: วันเริ่มต้น (YYYY-MM-DD)
end_date: วันสิ้นสุด (YYYY-MM-DD)
timeframe: Timeframe (1m, 5m, 1h, 1d)
"""
url = f"{BASE_URL}/ derivatives /{symbol}/candles"
params = {
"exchange": "deribit",
"start_date": start_date,
"end_date": end_date,
"timeframe": timeframe,
}
headers = {
"Authorization": f"Bearer {TARDIS_API_KEY}",
"Content-Type": "application/json"
}
response = requests.get(url, params=params, headers=headers)
if response.status_code == 401:
raise Exception("401 Unauthorized - ตรวจสอบ API key ของคุณ")
elif response.status_code == 429:
raise Exception("429 Rate Limited - รอสักครู่แล้วลองใหม่")
response.raise_for_status()
return response.json()
ทดสอบการดึงข้อมูล
try:
data = get_deribit_options_candles(
symbol="BTC-28MAR25-95000-C",
start_date="2025-03-01",
end_date="2025-03-28"
)
print(f"ดึงข้อมูลสำเร็จ: {len(data)} records")
except Exception as e:
print(f"เกิดข้อผิดพลาด: {e}")
ดาวน์โหลด CSV Options Data พร้อมสคริปต์สมบูรณ์
สำหรับคนที่ต้องการดาวน์โหลดข้อมูล options จำนวนมากเพื่อทำ backtest ผมเขียนสคริปต์ที่ครอบคลุมกว่านี้:
#!/usr/bin/env python3
"""
Deribit BTC/ETH Options Data Fetcher
ดึงข้อมูล options ทั้งหมดในช่วงเวลาที่กำหนด
"""
import requests
import pandas as pd
from datetime import datetime, timedelta
import time
import os
from typing import List, Dict, Optional
from dataclasses import dataclass
from concurrent.futures import ThreadPoolExecutor, as_completed
import json
@dataclass
class OptionsContract:
"""โครงสร้างข้อมูล options contract"""
symbol: str
underlying: str # BTC หรือ ETH
expiration: str
strike: float
option_type: str # C = Call, P = Put
exchange: str = "deribit"
class DeribitOptionsFetcher:
"""Fetcher สำหรับ Deribit options data"""
BASE_URL = "https://api.tardis-dev.com/v1"
def __init__(self, api_key: str, api_secret: str):
self.api_key = api_key
self.api_secret = api_secret
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
})
self.rate_limit_delay = 0.1 # 100ms between requests
self.retry_count = 3
self.retry_delay = 5 # seconds
def _make_request(self, endpoint: str, params: Dict) -> Dict:
"""ส่ง request พร้อม retry logic"""
for attempt in range(self.retry_count):
try:
response = self.session.get(
f"{self.BASE_URL}{endpoint}",
params=params
)
# Handle different error codes
if response.status_code == 401:
raise ConnectionError(
"401 Unauthorized - ตรวจสอบ API key"
)
elif response.status_code == 429:
wait_time = int(response.headers.get("Retry-After", 60))
print(f"Rate limited. รอ {wait_time} วินาที...")
time.sleep(wait_time)
continue
elif response.status_code == 500:
print(f"Server error (500). ลองใหม่... ({attempt + 1}/{self.retry_count})")
time.sleep(self.retry_delay)
continue
response.raise_for_status()
return response.json()
except requests.exceptions.Timeout:
print(f"Connection timeout. ลองใหม่... ({attempt + 1}/{self.retry_count})")
time.sleep(self.retry_delay)
except requests.exceptions.ConnectionError as e:
raise ConnectionError(f"ConnectionError: {str(e)}")
raise Exception(f"Request failed after {self.retry_count} attempts")
def get_all_options_symbols(
self,
underlying: str = "BTC",
start_date: str = "2025-01-01",
end_date: str = "2025-03-28"
) -> List[OptionsContract]:
"""ดึงรายชื่อ options symbols ทั้งหมด"""
data = self._make_request("/symbols", {
"exchange": "deribit",
"type": "option",
"underlying": underlying.lower()
})
contracts = []
for item in data.get("symbols", []):
# Parse symbol format: BTC-28MAR25-95000-C
parts = item["symbol"].split("-")
if len(parts) >= 4:
contracts.append(OptionsContract(
symbol=item["symbol"],
underlying=parts[0],
expiration=parts[1],
strike=float(parts[2]),
option_type=parts[3]
))
return contracts
def get_options_candles(
self,
symbol: str,
start_date: str,
end_date: str,
timeframe: str = "1h"
) -> pd.DataFrame:
"""ดึงข้อมูล OHLCV ของ option ตัวเดียว"""
data = self._make_request(f"/derivatives/{symbol}/candles", {
"exchange": "deribit",
"start_date": start_date,
"end_date": end_date,
"timeframe": timeframe
})
if not data or "candles" not in data:
return pd.DataFrame()
df = pd.DataFrame(data["candles"])
df["timestamp"] = pd.to_datetime(df["timestamp"], unit="ms")
df["symbol"] = symbol
return df
def download_batch(
self,
symbols: List[str],
start_date: str,
end_date: str,
output_dir: str = "./options_data",
max_workers: int = 5
) -> pd.DataFrame:
"""ดาวน์โหลดข้อมูลหลาย symbols พร้อมกัน"""
os.makedirs(output_dir, exist_ok=True)
all_data = []
total = len(symbols)
print(f"เริ่มดาวน์โหลด {total} symbols...")
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {
executor.submit(
self.get_options_candles,
symbol, start_date, end_date
): symbol for symbol in symbols
}
completed = 0
for future in as_completed(futures):
symbol = futures[future]
completed += 1
try:
df = future.result()
if not df.empty:
all_data.append(df)
# Save individual CSV
csv_path = os.path.join(output_dir, f"{symbol}.csv")
df.to_csv(csv_path, index=False)
print(f"[{completed}/{total}] ✓ {symbol}")
else:
print(f"[{completed}/{total}] ✗ {symbol} (ไม่มีข้อมูล)")
except Exception as e:
print(f"[{completed}/{total}] ✗ {symbol}: {e}")
time.sleep(self.rate_limit_delay)
# Merge และ save รวม
if all_data:
combined_df = pd.concat(all_data, ignore_index=True)
combined_path = os.path.join(output_dir, "all_options.csv")
combined_df.to_csv(combined_path, index=False)
print(f"\nบันทึกไฟล์รวม: {combined_path}")
return combined_df
return pd.DataFrame()
def export_to_csv(
self,
df: pd.DataFrame,
filename: str,
include_greeks: bool = True
):
"""Export DataFrame เป็น CSV พร้อม format ที่เหมาะกับ backtest"""
# เลือก columns ที่ต้องการ
columns = ["timestamp", "symbol", "open", "high", "low", "close", "volume"]
if include_greeks and "iv" in df.columns:
columns.extend(["iv", "delta", "gamma", "theta", "vega"])
export_df = df[[c for c in columns if c in df.columns]].copy()
# แปลง timestamp เป็น format ที่ต้องการ
export_df["timestamp"] = pd.to_datetime(export_df["timestamp"]).dt.strftime(
"%Y-%m-%d %H:%M:%S"
)
export_df.to_csv(filename, index=False)
print(f"Export CSV สำเร็จ: {filename}")
การใช้งาน
if __name__ == "__main__":
# ตั้งค่า credentials
fetcher = DeribitOptionsFetcher(
api_key="your_tardis_api_key",
api_secret="your_tardis_api_secret"
)
# ดึงรายชื่อ BTC options ทั้งหมด
btc_contracts = fetcher.get_all_options_symbols(
underlying="BTC",
start_date="2025-01-01",
end_date="2025-03-28"
)
# กรองเฉพาะ ATM options (strike ใกล้ spot ±5%)
symbols = [c.symbol for c in btc_contracts if 90000 <= c.strike <= 110000]
# ดาวน์โหลดข้อมูล
df = fetcher.download_batch(
symbols=symbols[:20], # Limit 20 symbols for demo
start_date="2025-03-01",
end_date="2025-03-28",
output_dir="./deribit_btc_options"
)
# Export CSV
if not df.empty:
fetcher.export_to_csv(df, "./deribit_btc_options/btc_options.csv")
เหมาะกับใคร / ไม่เหมาะกับใคร
| กลุ่มเป้าหมาย | ระดับความเหมาะสม | เหตุผล |
|---|---|---|
| Quantitative Researchers / Quant Funds | ★★★★★ | ต้องการข้อมูลครบถ้วนสำหรับ backtest กลยุทธ์ |
| Options Traders ที่ใช้กลยุทธ์ระยะยาว | ★★★★☆ | ใช้ข้อมูล history วิเคราะห์ IV และ greeks |
| นักศึกษาปริญญาตรี/โท | ★★★★☆ | เรียนรู้การทำ quant research ได้ดี |
| Swing Traders / Day Traders | ★★☆☆☆ | ต้องการ real-time มากกว่า historical |
| นักลงทุนรายย่อยทั่วไป | ★☆☆☆☆ | ใช้งานซับซ้อนเกินไป ค่าใช้จ่ายสูง |
ราคาและ ROI
| บริการ | ราคาเริ่มต้น/เดือน | จุดเด่น | ความคุ้มค่า |
|---|---|---|---|
| Tardis API (Direct) | $99-499/เดือน | ข้อมูลครบ, WebSocket support | เหมาะกับ professional use |
| HolySheep AI + Tardis | ¥50-200/เดือน | อัตราแลกเปลี่ยน ¥1=$1, API credits ฟรี | ประหยัด 85%+ |
| Deribit Direct API | ฟรี (limited) | ไม่มีค่าใช้จ่าย | Rate limit ต่ำ, data ไม่ครบ |
| Alternative Data Vendors | $500-2000/เดือน | ข้อมูล institutional grade | ราคาสูงเกินไปสำหรับรายบุคคล |
ทำไมต้องเลือก HolySheep
หลังจากทดลองใช้บริการหลายตัว ผมตัดสินใจใช้ HolySheep AI เพราะ:
- อัตราแลกเปลี่ยนพิเศษ: ¥1 = $1 ประหยัดเงินได้ถึง 85% เมื่อเทียบกับราคาปกติ
- Latency ต่ำมาก: ต่ำกว่า 50ms ทำให้ backtest รันเร็วขึ้นอย่างเห็นได้ชัด
- รองรับหลาย Models: เช่น GPT-4.1 ($8/MTok), Claude Sonnet 4.5 ($15/MTok), Gemini 2.5 Flash ($2.50/MTok), DeepSeek V3.2 ($0.42/MTok)
- เครดิตฟรีเมื่อลงทะเบียน: เริ่มทดลองใช้ได้ทันทีโดยไม่ต้องเติมเงินก่อน
- รองรับ WeChat/Alipay: จ่ายเงินได้สะดวกสำหรับคนไทยที่มี account
# ตัวอย่างการใช้ HolySheep API สำหรับ Options Analysis
import requests
import json
HolySheep API Configuration
BASE_URL = "https://api.holysheep.ai/v1"
API_KEY = "YOUR_HOLYSHEEP_API_KEY" # ได้จาก https://www.holysheep.ai/register
def analyze_options_with_ai(
options_data: dict,
analysis_type: str = "greeks_analysis"
) -> str:
"""
ใช้ AI วิเคราะห์ข้อมูล options
Args:
options_data: ข้อมูล options ที่ดึงมาจาก Tardis
analysis_type: ประเภทการวิเคราะห์
"""
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
# เตรียม prompt
if analysis_type == "greeks_analysis":
prompt = f"""
วิเคราะห์ Options Greeks จากข้อมูลต่อไปนี้:
Data: {json.dumps(options_data, indent=2)}
กรุณาวิเคราะห์:
1. Delta - ความไวของราคาต่อการเปลี่ยนแปลง spot
2. Gamma - อัตราการเปลี่ยนแปลงของ Delta
3. Theta - มูลค่าตามเวลาที่สูญเสียไป
4. Vega - ความไวต่อความผันผวน
แนะนำกลยุทธ์ที่เหมาะสม
"""
elif analysis_type == "strategy_backtest":
prompt = f"""
ทำ Backtest Analysis สำหรับกลยุทธ์ Options:
Historical Data: {json.dumps(options_data, indent=2)}
คำนวณ:
- Total Return
- Sharpe Ratio
- Max Drawdown
- Win Rate
เปรียบเทียบกับ Buy & Hold
"""
payload = {
"model": "gpt-4.1",
"messages": [
{"role": "system", "content": "คุณเป็นผู้เชี่ยวชาญด้าน Options Trading และ Quantitative Analysis"},
{"role": "user", "content": prompt}
],
"temperature": 0.3,
"max_tokens": 2000
}
response = requests.post(
f"{BASE_URL}/chat/completions",
headers=headers,
json=payload
)
if response.status_code == 401:
raise ConnectionError("401 Unauthorized - ตรวจสอบ API key")
response.raise_for_status()
result = response.json()
return result["choices"][0]["message"]["content"]
ตัวอย่างการใช้งาน
if __name__ == "__main__":
# ข้อมูล options ตัวอย่าง (จาก Tardis)
sample_data = {
"symbol": "BTC-28MAR25-95000-C",
"current_price": 2500,
"strike": 95000,
"spot_price": 94500,
"iv": 0.65,
"delta": 0.55,
"gamma": 0.002,
"theta": -45.20,
"vega": 12.30,
"time_to_expiry_days": 7
}
try:
result = analyze_options_with_ai(
options_data=sample_data,
analysis_type="greeks_analysis"
)
print("ผลการวิเคราะห์:")
print(result)
except Exception as e:
print(f"เกิดข้อผิดพลาด: {e}")
ข้อผิดพลาดที่พบบ่อยและวิธีแก้ไข
1. ConnectionError: timeout
สาเหตุ: Tardis API มี timeout ที่ 30 วินาที ถ้าดึงข้อมูลเยอะเกินไปจะ timeout
# วิธีแก้ไข: ใช้ chunking และ session ที่ถูกต้อง
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def create_robust_session() -> requests.Session:
"""สร้าง session ที่ทนต่อ timeout และ connection errors"""
session = requests.Session()
# Retry strategy
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504],
)
adapter = HTTPAdapter(
max_retries=retry_strategy,
pool_connections=10,
pool_maxsize=20
)
session.mount("http://", adapter)
session.mount("https://", adapter)
# เพิ่ม timeout ที่เหมาะสม
session.headers.update({
"timeout": 120 # 2 นาที
})
return session
ใช้ chunking สำหรับข้อมูลใหญ่
def fetch_in_chunks(symbol: str, start: str, end: str, chunk_days: int = 30):
"""ดึงข้อมูลเป็นช่วงๆ 30 วัน"""
from datetime import datetime, timedelta
start_date = datetime.strptime(start, "%Y-%m-%d")
end_date = datetime.strptime(end, "%Y-%m-%d")
all_data = []
current_start = start_date
while current_start < end_date:
current_end = min(current_start + timedelta(days=chunk_days), end_date)
try:
data = get_options_candles(
symbol,
current_start.strftime("%Y-%m-%d"),
current_end.strftime("%Y-%m-%d")
)
all_data.extend(data)
print(f"✓ ดึง {current_start.date()} ถึง {current_end.date()}")
except requests.exceptions.Timeout:
print(f"Timeout ที่ {current_start.date()} - ลองช่วงสั้นลง")
# ลองช่วงสั้นลง 15 วัน
data = fetch_in_chunks(
symbol,
current_start.strftime("%Y-%m-%d"),
(current_start + timedelta(days=15)).strftime("%Y-%m-%d")
)
all_data.extend(data)
current_start = current_end
return all_data
2. 401 Unauthorized - Invalid API Key
สาเหตุ: API key หมดอายุ, พิมพ์ผิด, หรือไม่มีสิทธิ์เข้าถึงข้อมูล options
# วิธีแก้ไข: ตรวจสอบและ refresh API key
import os
def validate_and_refresh_key():
"""ตรวจสอบความถูกต้องของ API key"""
# ลำดับการตรวจสอบ
key_sources = [
os.environ.get("TARDIS_API_KEY"),
"~/.tardis/key", # อ่านจากไฟล์ config
None
]
for key_source in key_sources:
if key_source and len(key_source) >= 32:
# ตรวจสอบ format
if key_source.startswith("td_"):
print(f"✓ API Key format ถูกต้อง")
# Test connection
try:
test_response = requests.get(
"https://api.tardis-dev.com/v1/account",
headers={"Authorization": f"Bearer {key_source}"}
)
if test_response.status_code == 200:
return key_source
elif test_response.status_code == 401:
print("⚠ API key หมดอายุหรือถูก revoke")
# ลอง refresh token
new_key = refresh_tardis_token()
if new_key:
return new_key
except Exception as e:
print(f"⚠ เชื่อมต่อไม่ได้: {e}")
raise ConnectionError("ไม่พบ API key ที่ถูกต้อง กรุณาต่ออายุที่ tardis-dev.com")
def refresh_tardis_token():
"""Refresh token อัตโนมัติ"""
# ต้องมี refresh token
refresh_token = os.environ.get("TARDIS_REFRESH_TOKEN")
if not refresh_token:
return None
try:
response = requests.post(
"https://api.tardis-dev.com/v1/auth/refresh",
json={"refresh_token": refresh_token}
)
if response.status_code == 200:
new_tokens = response.json()
# บันทึก token ใหม่
os.environ["TARDIS_API_KEY"] = new_tokens["access_token"]
# อัพเดทไฟล์ config
config_path = os.path.expanduser("~/.tardis/key")
os.makedirs(os.path.dirname(config_path), exist_ok=True)
with open(config_path, "w") as f:
f.write(new_tokens["access_token"])
print("✓ Refresh token สำเร็จ")
return new_tokens["access_token"]
except Exception as e:
print(f"⚠ Refresh ไม่สำเร็จ: {e}")
return None
3. 429 Rate Limited - ถูกจำกัดความเร็ว
สาเหตุ: เรียก API บ่อยเกินไปเกิน rate limit
# วิธีแก้ไข: Implement rate limiter อัจฉริยะ
import time
import threading
from collections import deque
from datetime import datetime, timedelta
class SmartRateLimiter:
"""Rate limiter ที่รองรับ burst