Conformal Prediction untuk Position Sizing yang Sadar Risiko
Setiap formula position sizing membutuhkan estimasi ketidakpastian. Kriteria Kelly membutuhkan probabilitas menang dan rasio payoff (lihat Kriteria Kelly untuk strategi). Optimasi mean-variance membutuhkan matriks kovarians. VaR membutuhkan distribusi return. Semua ini memerlukan asumsi tentang proses pembangkit data — asumsi yang secara rutin dilanggar oleh pasar keuangan.
Conformal prediction menawarkan sesuatu yang berbeda: interval prediksi dengan jaminan cakupan finite-sample, tanpa asumsi distribusi parametrik apa pun. Jika Anda meminta cakupan 90%, Anda mendapatkan setidaknya cakupan 90% — terlepas dari apakah return bersifat Gaussian, fat-tailed, skewed, atau heteroskedastik. Satu-satunya syarat adalah exchangeability (atau kondisi yang lebih lemah, seperti yang akan kita lihat).
Tulisan ini membahas teorinya, varian-varian utamanya, dan implementasi praktis untuk position sizing dalam Python.
Ide Inti: Skor Nonconformity

Conformal prediction bekerja dengan mengukur seberapa "aneh" suatu observasi baru relatif terhadap data masa lalu. Keanehan ini dikuantifikasi oleh skor nonconformity — fungsi apa pun yang mengukur seberapa buruk sebuah titik data sesuai dengan pola yang terlihat pada sisa data.
Untuk regresi (memprediksi nilai kontinu seperti return), skor nonconformity paling sederhana adalah residual absolut:
di mana adalah prediktor titik apa pun (regresi linier, random forest, neural network — tidak masalah) dan adalah sebuah titik data.
Wawasan kuncinya: jika titik-titik data bersifat exchangeable, maka peringkat dari di antara terdistribusi seragam atas . Ini adalah fakta yang murni kombinatorial — tidak memerlukan asumsi apa pun tentang distribusi atau .
Dari keseragaman peringkat ini, kita dapat menyusun interval prediksi dengan cakupan finite-sample.
Split Conformal Prediction

Split conformal prediction (Papadopoulos et al., 2002; Lei et al., 2018) adalah varian yang paling praktis. Algoritmanya sederhana:
Langkah 1. Bagi data menjadi training set dan calibration set .
Langkah 2. Fit model apa pun pada .
Langkah 3. Hitung skor nonconformity pada calibration set:
Langkah 4. Untuk tingkat miscoverage yang diinginkan , ambil sebagai kuantil empiris dari . Secara konkret, ini adalah residual terkecil ke- (dan kapan pun , yaitu untuk yang sangat kecil).
Langkah 5. Interval prediksi untuk titik baru adalah:
Jaminan Cakupan
Di bawah exchangeability data kalibrasi dan titik tes baru:
Ini adalah jaminan finite-sample — bukan aproksimasi asimptotik. Jaminan ini berlaku untuk model apa pun, distribusi data apa pun, dan ukuran sampel berapa pun. Jika adalah prediktor yang buruk, intervalnya akan sekadar menjadi lebih lebar. Jaminan cakupan tetap berlaku.
Ada juga batas atas ketika skor tidak memiliki ikatan (ties): , sehingga cakupannya tidak konservatif secara berlebihan.
Mengapa Ini Penting untuk Trading
Interval prediksi tradisional dari, katakanlah, regresi linier mengasumsikan galat Gaussian. Sebuah interval Gaussian yang dikalibrasi pada bagian utama data dapat sangat salah menilai ekor (tail) ketika residual sebenarnya bersifat heavy-tailed (misalnya Student- dengan derajat kebebasan yang sedikit): massa pusatnya lebih tipis daripada Gaussian, sehingga sebuah interval Gaussian yang dicocokkan dengan variansnya akan over-cover di dekat pusat namun under-cover di ekor, dan yang dicocokkan dengan ekor melakukan sebaliknya. Intinya bukanlah sebuah angka ajaib tunggal — intinya adalah bahwa cakupan yang terealisasi dari sebuah interval parametrik bergantung pada asumsi distribusi yang belum Anda verifikasi.
Interval conformal prediction menghindari masalah ini. Interval-interval tersebut secara otomatis melebar ketika model tidak yakin, dan mempertahankan cakupan marginal terlepas dari distribusi galat sebenarnya. Bagi seorang trader, ini berarti:
- Jika Anda menentukan ukuran posisi secara berbanding terbalik dengan lebar interval, Anda secara otomatis mengurangi eksposur ketika model tidak yakin.
- Jaminan cakupan berarti estimasi risiko Anda bersifat jujur — jika Anda mengatakan "90% dari return yang terealisasi akan berada dalam interval ini," pernyataan itu valid secara statistik (secara marginal, di bawah exchangeability).
Full Conformal dan Jackknife+
Split conformal sederhana tetapi memboroskan data: calibration set tidak dapat digunakan untuk training. Dua alternatif menangani hal ini.
Full Conformal Prediction
Full conformal prediction (Vovk et al., 2005) menggunakan semua data baik untuk training maupun kalibrasi. Untuk setiap nilai kandidat dari :
- Tambahkan dataset dengan .
- Fit ulang model pada dataset yang telah ditambah tersebut.
- Hitung semua skor nonconformity.
- Sertakan dalam himpunan prediksi jika skor untuk tidak terlalu ekstrem.
Himpunan prediksinya adalah:
di mana adalah skor nonconformity yang dihitung dengan dataset yang telah ditambah.
Full conformal memberikan interval yang paling ketat tetapi secara komputasi terlampau mahal untuk sebagian besar model — Anda harus fit ulang model untuk setiap kandidat pada sebuah grid. Untuk prediksi return, ini bisa berarti ribuan kali fit ulang per prediksi.
Jackknife+ (Barber et al., 2021)
Jackknife+ mencapai keseimbangan. Metode ini menggunakan residual leave-one-out (LOO) tetapi memperhitungkan variabilitas model yang di-fit lintas fold LOO.
Misalkan menyatakan model yang dilatih pada semua data kecuali titik . Definisikan skor nonconformity LOO dengan residual absolut tunggal:
Interval prediksi jackknife+ kemudian dibangun dari prediksi LOO pada titik tes, dilebarkan oleh residual-residual ini:
Di sini menyatakan nilai terkecil ke- dari himpunan , dan adalah nilai terkecil ke-. Batas bawah mengurangkan residual dari setiap prediksi LOO; batas atas menambahkannya. Asimetri itulah inti dari semuanya — meruntuhkan kedua batas menjadi akan menempatkan batas bawah di atas prediksi, yang mana itu salah.
Jackknife+ memberikan jaminan cakupan setidaknya (sedikit lebih lemah daripada milik split conformal), tetapi menggunakan semua data baik untuk training maupun kalibrasi. Dalam praktiknya cakupannya biasanya mendekati .
Untuk model trading yang dilatih pada data terbatas (misalnya, model spesifik-regime dengan hanya beberapa ratus observasi), jackknife+ seringkali merupakan pilihan terbaik — metode ini tidak mengorbankan data yang langka untuk kalibrasi. Biayanya adalah kali fit ulang model.
Masalah dengan Deret Waktu Keuangan: Non-Exchangeability

Jaminan conformal standar memerlukan exchangeability: distribusi gabungan dari bersifat invarian di bawah permutasi. Untuk data i.i.d., ini berlaku secara trivial.
Deret waktu keuangan tidak bersifat exchangeable. Return menunjukkan:
- Volatility clustering: Periode volatilitas tinggi mengikuti periode volatilitas tinggi (efek GARCH).
- Momentum dan mean reversion: Autokorelasi pada return atau return kuadrat.
- Perubahan regime: Distribusi bergeser seiring waktu (pasar bull vs. bear).
Jika Anda secara naif menerapkan split conformal pada deret waktu — menggunakan pemisahan kalibrasi acak — Anda melanggar struktur temporalnya. Skor kalibrasi dari tahun 2017 yang tenang tidak akan mencerminkan ketidakpastian tahun 2020 yang bergejolak. Jaminan cakupan Anda rusak.
Adaptive Conformal Inference (ACI)

Gibbs dan Candes (2021, NeurIPS) memperkenalkan Adaptive Conformal Inference (ACI) untuk menangani pergeseran distribusi dan data non-exchangeable. Idenya elegan: alih-alih menggunakan tingkat cakupan tetap, sesuaikan tingkat miscoverage target secara online berdasarkan apakah interval-interval terkini mencakup hasil sebenarnya, dan turunkan ulang kuantil dari distribusi skor pada setiap langkah.
Algoritma ACI
ACI tidak menggeser lebar interval secara langsung. Algoritma ini mempertahankan parameter miscoverage adaptif dan menghitung ulang kuantil conformal darinya. Pada setiap langkah waktu :
- Hitung ambang conformal sebagai kuantil empiris dari himpunan residual saat ini (skor kalibrasi, ditambah skor terealisasi sejauh ini): .
- Amati fitur , hasilkan interval .
- Amati nilai sebenarnya dan hitung indikator galat .
- Perbarui tingkat (bukan lebarnya):
di mana adalah ukuran langkah (step size) dan adalah miscoverage target. Jika sebuah interval meleset (), menyusut, yang mendorong kuantil berikutnya lebih tinggi dan melebarkan interval; jika interval mencakup, tumbuh dan interval mengetat. Yang krusial, di sini berada dalam satuan probabilitas — ia menggeser sebuah tingkat dalam , bukan ambang dalam satuan return mentah — sehingga yang sama berperilaku masuk akal entah residualnya berada pada orde atau tidak.
Jaminan Cakupan untuk ACI
ACI memberikan jaminan cakupan jangka panjang yang tidak bergantung pada model distribusi:
Karena di-clip ke , pembilangnya dibatasi oleh dikali sebuah konstanta, sehingga ruas kanannya adalah dan frekuensi miscoverage empiris konvergen ke . Pernyataan presisinya: ACI menjamin frekuensi miscoverage empiris jangka panjang konvergen ke untuk barisan sembarang (termasuk yang adversarial), asalkan tingkat yang diadaptasi tetap terbatas — yang ditegakkan oleh clip. Ini adalah jaminan atas frekuensi cakupan, bukan atas informativeness interval: di bawah barisan yang benar-benar adversarial, interval dapat tumbuh menjadi sangat lebar tanpa informasi sembari tetap mencapai target cakupan.
Dynamically-Tuned ACI (DtACI)
Gibbs dan Candes (2024, JMLR) menyempurnakan ACI dengan dynamic tuning dari ukuran langkah . Alih-alih menetapkan , mereka mempertahankan himpunan kandidat dan menggabungkannya melalui aturan agregasi-pakar (expert-aggregation), mengunggulkan yang cakupan terkininya paling dekat dengan target.
Ini menangani masalah praktis: yang besar beradaptasi cepat terhadap perubahan regime tetapi menghasilkan lebar interval yang bergejolak; yang kecil stabil tetapi lambat beradaptasi. DtACI menyeimbangkan keduanya secara otomatis.
Mengapa Ini Penting untuk Trading
Pertimbangkan sebuah strategi market-making yang menggunakan model peramalan return. Selama pasar tenang, interval conformal ketat — model percaya diri, dan Anda dapat mengambil posisi lebih besar. Ketika volatilitas melonjak (musim laporan keuangan, pengumuman FOMC, guncangan geopolitik), tingkat ACI beradaptasi dan interval melebar dalam beberapa langkah waktu. Position sizing Anda menyusut sebagai respons, tanpa model volatilitas eksplisit atau logika deteksi regime apa pun.
Inilah kuantifikasi ketidakpastian sebagai sinyal kelas-utama, bukan sebagai renungan belakangan.
Position Sizing dengan Interval Conformal

Sekarang mari kita hubungkan conformal prediction dengan position sizing konkret. Variabel kuncinya adalah setengah-lebar interval prediksi relatif terhadap kasus residual-absolut yang simetris. Dengan interval simetris , lebar penuhnya adalah . Untuk menjaga konsistensi formula dan kode, kita mengukur segalanya terhadap lebar penuh di sepanjang tulisan ini.
Inverse-Width Sizing
Pendekatan paling sederhana: tentukan ukuran secara berbanding terbalik dengan lebar interval.
di mana adalah konstanta penskalaan yang dikalibrasi terhadap anggaran risiko Anda. Ketika model percaya diri (interval sempit), Anda mengambil posisi lebih besar. Ketika tidak yakin (interval lebar), Anda mengambil yang lebih kecil.
Ini analog dengan volatility targeting (), tetapi dengan perbedaan krusial: lebar interval conformal adalah ukuran ketidakpastian bebas distribusi, bukan estimasi volatilitas parametrik. Ia menangkap ketidakpastian prediktif di bawah jaminan cakupan, bukan sekadar varians return.
Edge-Ratio Sizing dan Filter No-Trade
Inverse-width sizing murni mengabaikan kekuatan sinyal itu sendiri. Penyempurnaan yang alami adalah menskalakan dengan edge ratio — prediksi titik relatif terhadap lebar interval:
Ini adalah analog conformal dari rasio sinyal-terhadap-derau (signal-to-noise ratio): return harapan dibagi ukuran ketidakpastian bebas distribusi. Kami menggunakannya baik untuk sizing maupun untuk filter no-trade.
Filter ini berprinsip. Jika interval mengangkangi nol,
maka interval prediksi mencakup return positif maupun negatif — return terealisasi secara masuk akal dapat memiliki tanda yang berlawanan dari prediksi Anda. Definisikan ambang edge minimum dan trade hanya ketika . Perhatikan bahwa muatan geometris dari " cukup besar sehingga interval tidak lagi mengangkangi nol" tepatnya adalah (karena interval melewati nol ketika ). Pilih pada skala aktual dari melalui backtesting; untuk residual return harian, biasanya jauh di bawah , sehingga yang sangat kecil mungkin mengizinkan hampir semua trade dan yang besar mungkin tidak mengizinkan satu pun. Kalibrasikan terhadap data Anda.
Tentang "Conformal Kelly"
Menggiurkan untuk membaut interval conformal ke fraksi Kelly . Tetapi sudah merupakan fraksi yang lengkap dan terbatas yang diturunkan dari probabilitas menang dan rasio payoff ; mengalikannya dengan rasio tak terbatas seperti tidak memiliki justifikasi teori-keputusan — hasilnya dapat melebihi 1 atau membalik tanda secara independen dari , dan ini menghitung ganda edge yang sudah dikodekan oleh . Jadi kami tidak menyajikan pengali "conformal Kelly".
Jika Anda ingin menggerakkan Kelly dari interval, Anda harus benar-benar menurunkan dan darinya, yang memerlukan asumsi eksplisit tentang distribusi di dalam interval (interval conformal secara sengaja tidak mengatakan apa pun tentang itu — lihat Keterbatasan). Sebagai contoh, di bawah asumsi bentuk dalam-interval Anda dapat mengaproksimasi dan rasio payoff dari geometri interval — tetapi tandai asumsi itu dengan lantang, karena ia memperkenalkan kembali tepatnya komitmen parametrik yang seharusnya dihindari oleh conformal prediction.
Alternatif yang jujur dan ringan-asumsi adalah menggunakan edge ratio sebagai penyusutan fractional-Kelly: perbesar ukuran ketika return harapan besar relatif terhadap interval, perkecil ukuran ketika kecil, dan terapkan ini di atas batas keras (hard cap) — secara eksplisit sebagai heuristik, bukan sebagai "fraksi Kelly".
Implementasi Python
Berikut adalah implementasi praktis. Kami menunjukkan baik jalur split/prefit maupun jalur temporal (EnbPI), karena seluruh inti tulisan ini adalah bahwa data keuangan bersifat non-exchangeable.
Setup dan Persiapan Data
import numpy as np
import pandas as pd
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.model_selection import KFold
from mapie.regression import MapieRegressor, MapieTimeSeriesRegressor
from mapie.subsample import BlockBootstrap
def prepare_features(prices: pd.Series, lookback: int = 20) -> pd.DataFrame:
"""Create features from a price series."""
df = pd.DataFrame()
returns = prices.pct_change()
for lag in range(1, lookback + 1):
df[f"ret_lag_{lag}"] = returns.shift(lag)
for window in [5, 10, 20]:
df[f"ret_mean_{window}"] = returns.rolling(window).mean().shift(1)
df[f"ret_std_{window}"] = returns.rolling(window).std().shift(1)
df[f"ret_skew_{window}"] = returns.rolling(window).skew().shift(1)
df["target"] = returns.shift(-1)
return df.dropna()
Split Conformal dengan MAPIE (Prefit)
Untuk conformal split/prefit, cv="prefit" memerlukan method="base" (estimator split-conformal naif). Opsi method="plus" adalah estimator CV+/jackknife+ dan tidak kompatibel dengan cv="prefit" — ia memerlukan objek cross-validation sebagai gantinya. Kami menggunakan kombinasi yang benar di sini, dan menstandarkan sizing pada lebar penuh.
def split_conformal_sizing(
prices: pd.Series,
alpha: float = 0.1,
k: float = 1e-3, # scaling constant, in width units
max_position: float = 1.0,
min_edge: float = 0.05, # threshold on |pred| / width
) -> pd.DataFrame:
"""
Position sizing using split (prefit) conformal prediction intervals.
Sizing rule (consistent with the prose):
edge_t = |pred_t| / width_t
size_t = clip(k / width_t, 0, max_position) # inverse-width
size_t = 0 if edge_t < min_edge # no-trade filter
"""
df = prepare_features(prices)
X = df.drop(columns=["target"])
y = df["target"]
n_train = int(len(X) * 0.6)
n_cal = int(len(X) * 0.2)
X_train, y_train = X.iloc[:n_train], y.iloc[:n_train]
X_cal, y_cal = X.iloc[n_train:n_train + n_cal], y.iloc[n_train:n_train + n_cal]
X_test, y_test = X.iloc[n_train + n_cal:], y.iloc[n_train + n_cal:]
base_model = GradientBoostingRegressor(
n_estimators=200, max_depth=3, learning_rate=0.05, random_state=42,
)
base_model.fit(X_train, y_train)
mapie = MapieRegressor(estimator=base_model, cv="prefit", method="base")
mapie.fit(X_cal, y_cal)
y_pred, y_intervals = mapie.predict(X_test, alpha=alpha)
lower = y_intervals[:, 0, 0]
upper = y_intervals[:, 1, 0]
width = upper - lower
raw_size = k / np.where(width > 0, width, np.inf)
position_size = np.clip(raw_size, 0.0, max_position)
edge_ratio = np.abs(y_pred) / np.where(width > 0, width, np.inf)
position_size = np.where(edge_ratio < min_edge, 0.0, position_size)
position_size = position_size * np.sign(y_pred)
return pd.DataFrame({
"prediction": y_pred,
"lower": lower,
"upper": upper,
"width": width,
"edge_ratio": edge_ratio,
"position_size": position_size,
"actual": y_test.values,
}, index=X_test.index)
Conformal Deret Waktu dengan EnbPI
Karena return bersifat non-exchangeable, pemisahan acak/prefit di atas hanyalah sebuah baseline. MapieTimeSeriesRegressor dari MAPIE dengan method="enbpi" (Xu & Xie, 2021) menggunakan block bootstrap dan pembaruan residual yang dirancang untuk ketergantungan temporal. Inilah alat yang cocok dengan argumen tulisan ini sendiri.
def enbpi_sizing(
prices: pd.Series,
alpha: float = 0.1,
k: float = 1e-3,
max_position: float = 1.0,
min_edge: float = 0.05,
) -> pd.DataFrame:
"""Position sizing with EnbPI (block-bootstrap, time-series conformal)."""
df = prepare_features(prices)
X = df.drop(columns=["target"])
y = df["target"]
n_train = int(len(X) * 0.7)
X_train, y_train = X.iloc[:n_train], y.iloc[:n_train]
X_test, y_test = X.iloc[n_train:], y.iloc[n_train:]
base_model = GradientBoostingRegressor(
n_estimators=200, max_depth=3, learning_rate=0.05, random_state=42,
)
cv = BlockBootstrap(n_resamplings=30, length=20, overlapping=False, random_state=42)
mapie_ts = MapieTimeSeriesRegressor(base_model, method="enbpi", cv=cv, agg_function="mean")
mapie_ts.fit(X_train, y_train)
y_pred, y_intervals = mapie_ts.predict(X_test, alpha=alpha, ensemble=True)
lower = y_intervals[:, 0, 0]
upper = y_intervals[:, 1, 0]
width = upper - lower
raw_size = k / np.where(width > 0, width, np.inf)
position_size = np.clip(raw_size, 0.0, max_position)
edge_ratio = np.abs(y_pred) / np.where(width > 0, width, np.inf)
position_size = np.where(edge_ratio < min_edge, 0.0, position_size)
position_size = position_size * np.sign(y_pred)
return pd.DataFrame({
"prediction": y_pred, "lower": lower, "upper": upper,
"width": width, "edge_ratio": edge_ratio,
"position_size": position_size, "actual": y_test.values,
}, index=X_test.index)
Adaptive Conformal Inference (Online)
Untuk live trading kami mengimplementasikan ACI sejati dari nol: pertahankan tingkat miscoverage , perbarui secara aditif, dan turunkan ulang kuantil dari himpunan residual setiap langkah. Dua detail finite-sample penting:
- Gunakan statistik urutan (order statistic), bukan kuantil yang diinterpolasi.
np.quantilemelakukan interpolasi secara default, yang dapat jatuh tepat di bawah peringkat yang diperlukan dan under-cover; lewatkanmethod="higher"(setara dengan"inverted_cdf"). - Ketika peringkat yang diperlukan melebihi ( kecil, cakupan target tinggi), ambang yang benar adalah (interval = seluruh garis), bukan clamp ke residual terbesar. Clamping diam-diam merusak jaminan .
class AdaptiveConformalSizer:
"""
Online position sizing with Adaptive Conformal Inference (Gibbs & Candes,
2021). Updates the miscoverage LEVEL alpha_t and re-derives the quantile
from the residual set each step -- gamma is in probability units.
"""
def __init__(self, base_model, alpha=0.1, gamma=0.02,
max_position=1.0, min_edge=0.05, k=1e-3):
self.base_model = base_model
self.alpha_target = alpha # target miscoverage
self.alpha_t = alpha # adaptive miscoverage level
self.gamma = gamma # step size, in [0, 1] units
self.max_position = max_position
self.min_edge = min_edge
self.k = k
self.residuals = []
self.q_hat = np.inf
self.coverage_history = []
@staticmethod
def _conformal_quantile(residuals, alpha_t):
"""(1 - alpha_t) conformal quantile via the order statistic."""
n = len(residuals)
if n == 0:
return np.inf
rank = int(np.ceil((1.0 - alpha_t) * (n + 1)))
if rank > n: # required order statistic does not exist
return np.inf # -> interval is the whole line
level = rank / n
return float(np.quantile(residuals, min(level, 1.0), method="higher"))
def calibrate(self, X_cal, y_cal):
preds = self.base_model.predict(X_cal)
self.residuals = list(np.abs(np.asarray(y_cal) - preds))
self.q_hat = self._conformal_quantile(self.residuals, self.alpha_t)
def predict_and_size(self, X_t) -> dict:
mu_hat = self.base_model.predict(np.asarray(X_t).reshape(1, -1))[0]
lower, upper = mu_hat - self.q_hat, mu_hat + self.q_hat
width = upper - lower # = 2 * q_hat
edge_ratio = abs(mu_hat) / width if np.isfinite(width) and width > 0 else 0.0
if edge_ratio < self.min_edge:
size = 0.0
else:
size = min(self.k / width, self.max_position) if width > 0 else 0.0
return {
"prediction": mu_hat, "lower": lower, "upper": upper,
"width": width, "edge_ratio": edge_ratio,
"position_size": size * np.sign(mu_hat),
"alpha_t": self.alpha_t, "q_hat": self.q_hat,
}
def update(self, X_t, y_t: float):
"""ACI update: adapt the LEVEL, then re-derive the quantile."""
mu_hat = self.base_model.predict(np.asarray(X_t).reshape(1, -1))[0]
residual = abs(y_t - mu_hat)
covered = int(residual <= self.q_hat)
err_t = 1 - covered
self.alpha_t = float(np.clip(
self.alpha_t + self.gamma * (self.alpha_target - err_t), 0.0, 1.0
))
self.residuals.append(residual)
self.q_hat = self._conformal_quantile(self.residuals, self.alpha_t)
self.coverage_history.append(covered)
@property
def running_coverage(self) -> float:
if not self.coverage_history:
return float("nan")
return float(np.mean(self.coverage_history))
Menyatukan Semuanya: Loop Backtest
def backtest_aci_sizing(prices: pd.Series, alpha=0.1, gamma=0.02) -> pd.DataFrame:
"""Backtest position sizing with Adaptive Conformal Inference."""
df = prepare_features(prices)
X = df.drop(columns=["target"]).values
y = df["target"].values
index = df.index
n_train = int(len(X) * 0.5)
n_cal = int(len(X) * 0.2)
X_train, y_train = X[:n_train], y[:n_train]
X_cal, y_cal = X[n_train:n_train + n_cal], y[n_train:n_train + n_cal]
X_test, y_test = X[n_train + n_cal:], y[n_train + n_cal:]
test_index = index[n_train + n_cal:]
model = GradientBoostingRegressor(
n_estimators=200, max_depth=3, learning_rate=0.05, random_state=42,
)
model.fit(X_train, y_train)
sizer = AdaptiveConformalSizer(base_model=model, alpha=alpha, gamma=gamma)
sizer.calibrate(X_cal, y_cal)
records = []
for i in range(len(X_test)):
result = sizer.predict_and_size(X_test[i])
result["actual"] = y_test[i]
result["pnl"] = result["position_size"] * y_test[i]
records.append(result)
sizer.update(X_test[i], y_test[i]) # online residual + level update
results = pd.DataFrame(records, index=test_index)
results["cumulative_pnl"] = results["pnl"].cumsum()
results["running_coverage"] = (
((results["lower"] <= results["actual"]) &
(results["actual"] <= results["upper"])).expanding().mean()
)
return results
Mengevaluasi Hasilnya
def evaluate(results: pd.DataFrame, alpha: float = 0.1):
"""Print evaluation metrics for conformal position sizing."""
covered = ((results["actual"] >= results["lower"]) &
(results["actual"] <= results["upper"]))
print(f"Target coverage: {1 - alpha:.1%}")
print(f"Empirical coverage: {covered.mean():.1%}")
print(f"Mean interval width: {results['width'].mean():.6f}")
print(f"Median position size: {results['position_size'].abs().median():.4f}")
print(f"Fraction no-trade: {(results['position_size'] == 0).mean():.1%}")
print(f"Total PnL (bps): {results['pnl'].sum() * 10000:.1f}")
sd = results['pnl'].std()
sharpe = results['pnl'].mean() / sd * np.sqrt(252) if sd > 0 else float('nan')
print(f"Sharpe (annualized): {sharpe:.2f}")
Pertimbangan Praktis
Memilih Skor Nonconformity
Residual absolut adalah default, tetapi ia mengasumsikan interval prediksi seharusnya simetris di sekitar prediksi titik. Untuk return keuangan, interval asimetris seringkali lebih masuk akal:
- Conformalized Quantile Regression (CQR): Fit quantile regressor pada tingkat dan , lalu conformalize (Romano et al., 2019). Interval-interval tersebut menyesuaikan bentuknya terhadap distribusi lokal — lebih lebar di sisi bawah selama drawdown, lebih lebar di sisi atas selama rally. (Dengan CQR interval tidak lagi simetris, sehingga adalah lebar atas-dikurangi-bawah yang sebenarnya — tetap gunakan sebagai penyebut di mana pun.)
- Skor ternormalisasi: , di mana adalah estimasi volatilitas lokal. Ini menghasilkan interval yang adaptif secara kondisional — lebih ketat selama regime volatilitas rendah, lebih lebar selama yang tinggi — sembari mempertahankan cakupan marginal.
Ukuran Calibration Set
Jaminan cakupan dari split conformal berlaku untuk ukuran calibration set berapa pun, tetapi lebar interval mengecil seiring bertambahnya . Untuk yang sangat kecil, statistik urutan yang diperlukan mungkin tidak ada, dalam hal ini ambang yang jujur adalah (interval yang tidak informatif tetapi valid) — clamping ke residual terbesar diam-diam membatalkan jaminan. Untuk trading praktis:
- titik kalibrasi memberikan interval yang cukup ketat.
- lebih disukai untuk estimasi kuantil yang stabil.
- Dengan ACI, calibration set hanya untuk inisialisasi; pembaruan tingkat online menangani sisanya.
Frekuensi Pelatihan Ulang
Model dasar dapat menjadi usang. Dua pendekatan:
- Latih ulang secara periodik (misalnya, bulanan) dan kalibrasi ulang kuantil conformal.
- Gunakan ACI dan biarkan tingkat adaptif mengompensasi keusangan model — interval melebar secara otomatis seiring bertambahnya residual model.
Opsi 2 lebih sederhana dan secara mengejutkan efektif. Lapisan conformal bertindak sebagai jaring pengaman: bahkan jika model menurun kualitasnya, frekuensi cakupan ACI jangka panjang tetap konvergen ke target.
Biaya Transaksi
Interval conformal berinteraksi dengan biaya transaksi dengan cara yang berguna. Ketika interval lebar (ketidakpastian tinggi), posisi kecil, sehingga turnover rendah. Ketika interval menyempit (model percaya diri), posisi tumbuh — tetapi model lebih mungkin benar, sehingga turnover-nya layak dibayar.
Anda juga dapat memasukkan biaya transaksi langsung ke dalam filter no-trade:
Ini memastikan Anda hanya trade ketika edge bersih yang diharapkan melebihi sebuah ambang yang diskalakan oleh lebar conformal — menggunakan penyebut yang sama seperti di mana pun.
Perbandingan dengan Metode Tradisional
| Properti | Gaussian CI | Bootstrap CI | Conformal PI |
|---|---|---|---|
| Asumsi distribusi | Galat normal | i.i.d. + asimptotik | Tidak ada (exchangeability) |
| Jaminan finite-sample | Tidak | Tidak | Ya |
| Bekerja dengan model apa pun | Tidak | Ya | Ya |
| Beradaptasi terhadap heteroskedastisitas | Tidak | Sebagian | Dengan CQR / skor ternormalisasi |
| Menangani pergeseran distribusi | Tidak | Tidak | Varian ACI / EnbPI |
| Biaya komputasi | Rendah | Sedang | Split: rendah; jackknife+: fit ulang; full: terlampau mahal |
Bootstrap bersifat "asimptotik" hanya dalam jaminannya; ia tetap mengasumsikan data i.i.d./exchangeable dan kemulusan, sehingga sel asumsi-distribusinya bukanlah "bebas-asumsi". Dan kolom tunggal "conformal" menyembunyikan biaya yang sangat berbeda antar varian, yang sekarang dijabarkan oleh baris biaya.
Keterbatasan
Conformal prediction bukanlah sihir. Keterbatasan utama untuk trading:
-
Cakupan marginal, bukan kondisional. Jaminannya adalah bahwa secara marginal — dirata-ratakan atas keacakan baik dalam maupun . Untuk nilai pengkondisian spesifik , cakupan lokal bisa di atas atau di bawah . Conformalized quantile regression sebagian menangani hal ini.
-
Exchangeability adalah syarat nyata. Untuk split conformal, data kalibrasi dan titik tes harus exchangeable. Data keuangan tidak. ACI dan EnbPI melonggarkan ini menjadi jaminan jangka panjang, tetapi cakupan jangka pendek dapat menyimpang.
-
Lebar interval bukanlah densitas probabilitas. Sebuah interval conformal memberi tahu Anda di mana akan jatuh dengan probabilitas , tetapi tidak mengatakan apa pun tentang distribusi di dalam interval. Ia bukan pengganti distribusi prediktif penuh — yang justru menjadi alasan mengapa menurunkan dan Kelly darinya memerlukan asumsi eksplisit tambahan.
-
Sampah masuk, lebih lebar keluar. Model dasar yang buruk menghasilkan interval yang lebar. Conformal prediction menjamin cakupan tetapi bukan kegunaan. Jika model Anda tidak memiliki daya prediktif, intervalnya akan begitu lebar sehingga position sizer tidak pernah trade.
Ringkasan
Conformal prediction menyediakan kerangka kerja yang ketat dan bebas distribusi untuk kuantifikasi ketidakpastian yang menyatu secara alami ke dalam position sizing:
- Split conformal untuk kalibrasi statis dan offline dengan cakupan finite-sample.
- Jackknife+ ketika data kalibrasi langka dan Anda ingin menggunakan semua observasi (dengan biaya kali fit ulang).
- Adaptive conformal inference / EnbPI untuk trading online dengan pasar non-stasioner.
- Position sizing melalui inverse lebar interval dan filter no-trade berbasis edge-ratio — dan, jika Anda beralih ke Kelly, hanya setelah menurunkan dan secara jujur, bukan melalui pengali yang tidak terjustifikasi.
Keunggulan kunci atas alternatif parametrik: Anda tidak pernah perlu menentukan atau memvalidasi asumsi distribusi parametrik. Interval-interval tersebut jujur secara konstruksi (secara marginal, di bawah exchangeability). Bagi seorang trader sistematis, ini berarti satu sumber risiko model yang lebih sedikit — dan dalam bisnis di mana risiko model bersifat eksistensial, itu penting.
Referensi:
- Papadopoulos, H., Proedrou, K., Vovk, V., Gammerman, A. (2002). Inductive confidence machines for regression. ECML.
- Vovk, V., Gammerman, A., Shafer, G. (2005). Algorithmic Learning in a Random World. Springer.
- Lei, J., G'Sell, M., Rinaldo, A., Tibshirani, R.J., Wasserman, L. (2018). Distribution-free predictive inference for regression. JASA.
- Xu, C., Xie, Y. (2021). Conformal prediction interval for dynamic time-series (EnbPI). ICML.
- Barber, R.F., Candes, E.J., Ramdas, A., Tibshirani, R.J. (2021). Predictive inference with the jackknife+. Annals of Statistics.
- Gibbs, I., Candes, E.J. (2021). Adaptive conformal inference under distribution shift. NeurIPS.
- Gibbs, I., Candes, E.J. (2024). Conformal inference for online prediction with arbitrary distribution shifts. JMLR.
- Romano, Y., Patterson, E., Candes, E.J. (2019). Conformalized quantile regression. NeurIPS.
- Cordier, T. et al. (2022). MAPIE: an open-source library for distribution-free uncertainty quantification. arXiv:2207.12274.
Penulis
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.