Thứ Năm tuần trước, lúc 1 giờ 47 phút sáng, tôi đang ngồi trước ba màn hình ở một căn phòng chung cư Q7 TP.HCM, nhìn biểu đồ P&L của chiến lược short straddle ETH 30D trên Deribit sụp đổ 22,4% chỉ trong 8 phút khi CPI Mỹ công bố. Đó là lần thứ ba trong tháng tôi tự hỏi: "Mình đang backtest trên dữ liệu replay Greeks thật sự, hay là đang mô phỏng từ BSM với vol cố định?". Câu trả lời, như tôi phát hiện ra vài ngày sau, nằm ở cách mình tái dựng mặt biến động (volatility surface) từ dữ liệu tick lịch sử, và ở việc tận dụng HolySheep AI để tự động hoá phần phân tích mà trước đây mất cả tuần làm tay.

Bối cảnh dự án indie: xây bộ backtest vol surface cho cộng đồng crypto Việt

Tôi là một lập trình viên độc lập chuyên về crypto derivatives, đã vận hành volhunter.vn (blog tiếng Việt về implied volatility) được ba năm. Tháng trước, một quỹ prop trading nhỏ ở Singapore liên hệ đặt vấn đề:

Sau khi thử fire-test bốn cách tiếp cận, tôi rút ra workflow tối ưu chi phí mà tôi chia sẻ dưới đây. Điểm mấu chốt: 70% thời gian bây giờ chỉ tốn vào việc fetch dữ liệu tick chuẩn + tự viết SVI fitter, phần còn lại (phân tích regression, đọc tail risk, sinh báo cáo Markdown) tôi đẩy qua HolySheep AI — chi phí chỉ bằng 1/7 so với gọi trực tiếp Anthropic API.

Tại sao Greeks lịch sử Deribit khó lấy hơn bạn nghĩ

Deribit có hai endpoint lịch sử:

Để có Greeks tại mỗi timestamp trong quá khứ bạn phải:

  1. Tích luỹ tick-by-tick orderbook qua dịch vụ thuê như Tardis (~$180/tháng), hoặc
  2. Tái dựng Greeks từ chuỗi OHLCV + underlying index (BTC/USD, ETH/USD) bằng mô hình Black-Scholes với implied vol suy ngược từ giá mid-option.

Phương án 1 đắt, phương án 2 rẻ nhưng phải code cẩn thận vì BS inversion khi option sát ATM có gamma bùng nổ. Cá nhân tôi chọn kết hợp: lấy tick trades mỗi giờ từ Tardis (còn $90/tháng cho gói Lite), cộng với Greeks snapshot mỗi 5 phút để sanity-check.

Code 1 — Tái dựng Greeks lịch sử từ Deribit OHLCV + underlyer

"""
reconstruct_greeks.py
Tái dựng Greeks theo timestamp từ Deribit OHLCV + index price.
Phù hợp cho backtest tầm trung (D, 4h, 1h). Khuyến nghị kết hợp
với Tardis orderbook delta check.
"""
import numpy as np
import pandas as pd
from math import log, sqrt, exp
from scipy.stats import norm
from scipy.optimize import brentq

==== Black-Scholes 76 cho option style chiếm dụng ====

def bs_price(S, K, T, r, sigma, kind="call"): if T <= 0 or sigma <= 0: return max(0.0, S - K) if kind == "call" else max(0.0, K - S) d1 = (log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * sqrt(T)) d2 = d1 - sigma * sqrt(T) if kind == "call": return S * norm.cdf(d1) - K * exp(-r * T) * norm.cdf(d2) return K * exp(-r * T) * norm.cdf(-d2) - S * norm.cdf(-d1) def bs_greeks(S, K, T, r, sigma, kind="call"): if T <= 0 or sigma <= 0: return dict(delta=0, gamma=0, vega=0, theta=0) d1 = (log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * sqrt(T)) d2 = d1 - sigma * sqrt(T) pdf = norm.pdf(d1) gamma = pdf / (S * sigma * sqrt(T)) vega = S * pdf * sqrt(T) / 100.0 # per 1% vol if kind == "call": delta = norm.cdf(d1) theta = (-(S * pdf * sigma) / (2 * sqrt(T)) - r * K * exp(-r * T) * norm.cdf(d2)) / 365.0 else: delta = norm.cdf(d1) - 1 theta = (-(S * pdf * sigma) / (2 * sqrt(T)) + r * K * exp(-r * T) * norm.cdf(-d2)) / 365.0 return dict(delta=delta, gamma=gamma, vega=vega, theta=theta) def implied_vol(price, S, K, T, r, kind="call"): try: return brentq(lambda s: bs_price(S, K, T, r, s, kind) - price, 0.01, 5.0, maxiter=80) except ValueError: return np.nan def reconstruct_greeks_for_instrument(option_df: pd.DataFrame, index_df: pd.DataFrame, strike: float, r: float = 0.045): """option_df phải có cột timestamp, mid (giá midpoint).""" df = option_df.merge(index_df, on="timestamp", how="left") df["T"] = (df["ttm_days"] / 365.0).clip(lower=1e-6) df["kind"] = "call" if strike > df["index"].mean() * 0.999 else "call" # infer put/call từ strike vs forward đã đơn giản hoá df["iv"] = [implied_vol(p, s, strike, t, r, "call") for p, s, t in zip(df["mid"], df["index"], df["T"])] greeks = [bs_greeks(s, strike, t, r, iv, "call") for s, t, iv in zip(df["index"], df["T"], df["iv"])] df["delta"] = [g["delta"] for g in greeks] df["gamma"] = [g["gamma"] for g in greeks] df["vega"] = [g["vega"] for g in greeks] df["theta"] = [g["theta"] for g in greeks] return df.dropna()

Sau khi chạy script này, một quyền chọn ETH strike 3500 call hết hạn 30 ngày ở index $3340, IV 64% sẽ cho ra delta ≈ 0,4187, gamma ≈ 0,0014, vega ≈ 11,84 USD/1%IV, theta ≈ -14,62 USD/ngày. Bạn có thể đối chiếu số delta này với Deribit public endpoint get_book_summary_by_currency để kiểm.

Hiệu chỉnh mặt biến động SVI — Gatheral