← Kembali ke artikel
June 12, 2026
Bacaan 5 minit

Conformal Prediction untuk Penentuan Saiz Posisi yang Sedar Risiko

Conformal Prediction untuk Penentuan Saiz Posisi yang Sedar Risiko
#ketidakpastian
#conformal-prediction
#risiko
#position-sizing
#statistik
#algorithmic-trading
🧠
Part 4 of 4 · Collection
Deep Learning for Markets

Setiap formula penentuan saiz posisi memerlukan anggaran ketidakpastian. Kriteria Kelly memerlukan kebarangkalian menang dan nisbah ganjaran (lihat Kriteria Kelly untuk strategi). Pengoptimuman min-varians memerlukan matriks kovarians. VaR memerlukan taburan pulangan. Kesemuanya memerlukan andaian tentang proses penjana data — andaian yang lazimnya dilanggar oleh pasaran kewangan.

Conformal prediction menawarkan sesuatu yang berbeza: selang ramalan dengan jaminan liputan sampel terhingga, tanpa sebarang andaian taburan parametrik. Jika anda meminta liputan 90%, anda akan mendapat sekurang-kurangnya liputan 90% — tidak kira sama ada pulangan adalah Gaussian, berekor gemuk, terpencong, atau heteroskedastik. Satu-satunya syarat ialah kebolehtukaran (exchangeability), atau syarat yang lebih lemah, seperti yang akan kita lihat.

Catatan ini merangkumi teori, varian utama, dan pelaksanaan praktikal untuk penentuan saiz posisi dalam Python.

Idea Teras: Skor Nonconformity

Taburan skor nonconformity (residual) dengan ambang kuantil ditanda — teras kalibrasi conformal

Conformal prediction berfungsi dengan mengukur sejauh mana "pelik" sesuatu pemerhatian baharu berbanding data lampau. Tahap kepelikan ini dikuantifikasikan oleh skor nonconformity — sebarang fungsi yang mengukur sejauh mana lemah suatu titik data mematuhi corak yang dilihat dalam baki data.

Untuk regresi (meramalkan nilai berterusan seperti pulangan), skor nonconformity yang paling ringkas ialah residual mutlak:

Ri=Yiμ^(Xi)R_i = |Y_i - \hat{\mu}(X_i)|

di mana μ^\hat{\mu} ialah sebarang peramal titik (regresi linear, random forest, rangkaian neural — ia tidak penting) dan (Xi,Yi)(X_i, Y_i) ialah satu titik data.

Wawasan utamanya: jika titik data (X1,Y1),,(Xn,Yn),(Xn+1,Yn+1)(X_1, Y_1), \ldots, (X_n, Y_n), (X_{n+1}, Y_{n+1}) boleh ditukar (exchangeable), maka pangkat Rn+1R_{n+1} dalam kalangan R1,,Rn,Rn+1R_1, \ldots, R_n, R_{n+1} adalah teragih seragam ke atas {1,,n+1}\{1, \ldots, n+1\}. Ini ialah fakta kombinatorik tulen — ia tidak memerlukan sebarang andaian tentang taburan XX atau YY.

Daripada keseragaman pangkat ini, kita boleh membina selang ramalan dengan liputan sampel terhingga.

Split Conformal Prediction

Split conformal prediction: data dipecah kepada lipatan latihan dan kalibrasi, residual kalibrasi menghasilkan selang ramalan

Split conformal prediction (Papadopoulos et al., 2002; Lei et al., 2018) ialah varian yang paling praktikal. Algoritmanya ringkas:

Langkah 1. Pecahkan data kepada set latihan Dtrain\mathcal{D}_{\text{train}} dan set kalibrasi Dcal={(X1,Y1),,(Xn,Yn)}\mathcal{D}_{\text{cal}} = \{(X_1, Y_1), \ldots, (X_n, Y_n)\}.

Langkah 2. Suaikan sebarang model μ^\hat{\mu} pada Dtrain\mathcal{D}_{\text{train}}.

Langkah 3. Kira skor nonconformity pada set kalibrasi:

Ri=Yiμ^(Xi),i=1,,nR_i = |Y_i - \hat{\mu}(X_i)|, \quad i = 1, \ldots, n

Langkah 4. Untuk paras kesilapan liputan (miscoverage) yang dikehendaki α(0,1)\alpha \in (0, 1), ambil q^\hat{q} sebagai kuantil empirik (1α)(n+1)n\frac{\lceil (1 - \alpha)(n + 1) \rceil}{n} bagi R1,,RnR_1, \ldots, R_n. Secara konkrit, ini ialah residual terkecil ke-(1α)(n+1)\lceil (1 - \alpha)(n + 1) \rceil (dan q^=+\hat{q} = +\infty apabila (1α)(n+1)>n\lceil (1 - \alpha)(n + 1) \rceil > n, iaitu untuk nn yang sangat kecil).

Langkah 5. Selang ramalan untuk titik baharu Xn+1X_{n+1} ialah:

C(Xn+1)=[μ^(Xn+1)q^,  μ^(Xn+1)+q^]C(X_{n+1}) = \left[\hat{\mu}(X_{n+1}) - \hat{q}, \; \hat{\mu}(X_{n+1}) + \hat{q}\right]

Jaminan Liputan

Di bawah kebolehtukaran data kalibrasi dan titik ujian baharu:

P(Yn+1C(Xn+1))1α\mathbb{P}\left(Y_{n+1} \in C(X_{n+1})\right) \geq 1 - \alpha

Ini ialah jaminan sampel terhingga — bukan anggaran asimptot. Ia sah untuk sebarang model μ^\hat{\mu}, sebarang taburan data, dan sebarang saiz sampel nn. Jika μ^\hat{\mu} ialah peramal yang teruk, selang itu sekadar akan menjadi lebih lebar. Jaminan liputan masih tetap berlaku.

Terdapat juga batas atas apabila skor tidak mempunyai nilai seri: P(Yn+1C(Xn+1))1α+1n+1\mathbb{P}(Y_{n+1} \in C(X_{n+1})) \leq 1 - \alpha + \frac{1}{n+1}, jadi liputan itu tidak konservatif secara membazir.

Mengapa Ini Penting untuk Dagangan

Selang ramalan tradisional daripada, katakan, regresi linear mengandaikan ralat Gaussian. Selang Gaussian yang dikalibrasi pada majoriti data boleh tersalah anggap ekor dengan teruk apabila residual sebenar berekor berat (cth. Student-tt dengan beberapa darjah kebebasan): jisim tengah lebih nipis berbanding Gaussian, jadi selang Gaussian yang dipadankan-varians akan terlebih-liputan berhampiran pusat namun terkurang-liputan di ekor, dan selang yang disuaikan-ekor pula melakukan sebaliknya. Maksudnya bukan satu nombor ajaib tunggal — tetapi bahawa liputan teralami bagi selang parametrik bergantung pada andaian taburan yang belum anda sahkan.

Selang conformal prediction mengelakkan masalah ini. Ia secara automatik melebar apabila model tidak pasti, dan ia mengekalkan liputan marginal tanpa mengira taburan ralat sebenar. Bagi seorang pedagang, ini bermaksud:

  • Jika anda menentukan saiz posisi secara berkadar songsang dengan lebar selang, anda secara automatik mengurangkan pendedahan apabila model tidak pasti.
  • Jaminan liputan bermaksud anggaran risiko anda adalah jujur — jika anda berkata "90% pulangan teralami akan jatuh dalam selang ini," kenyataan itu sah secara statistik (secara marginal, di bawah kebolehtukaran).

Full Conformal dan Jackknife+

Split conformal adalah ringkas tetapi membazir data: set kalibrasi tidak boleh digunakan untuk latihan. Dua alternatif menangani perkara ini.

Full Conformal Prediction

Full conformal prediction (Vovk et al., 2005) menggunakan kesemua data untuk kedua-dua latihan dan kalibrasi. Bagi setiap nilai calon yy daripada Yn+1Y_{n+1}:

  1. Tambah set data dengan (Xn+1,y)(X_{n+1}, y).
  2. Suaikan semula model pada set data yang ditambah itu.
  3. Kira semua skor nonconformity.
  4. Sertakan yy dalam set ramalan jika skor untuk (Xn+1,y)(X_{n+1}, y) tidak terlalu melampau.

Set ramalannya ialah:

C(Xn+1)={y:{i:RiyRn+1y}n+1>α}C(X_{n+1}) = \left\{y : \frac{|\{i : R_i^y \geq R_{n+1}^y\}|}{n+1} > \alpha \right\}

di mana RiyR_i^y ialah skor nonconformity yang dikira dengan set data yang ditambah.

Full conformal memberikan selang yang paling ketat tetapi terlalu mahal dari segi pengkomputeran untuk kebanyakan model — anda mesti menyuaikan semula model untuk setiap calon yy pada satu grid. Untuk ramalan pulangan, ini boleh bermaksud ribuan suaian semula bagi setiap ramalan.

Jackknife+ (Barber et al., 2021)

Jackknife+ mencapai keseimbangan. Ia menggunakan residual leave-one-out (LOO) tetapi mengambil kira variabiliti dalam model tersuai merentas lipatan LOO.

Biarkan μ^i\hat{\mu}_{-i} melambangkan model yang dilatih pada kesemua data kecuali titik ii. Takrifkan skor nonconformity LOO dengan residual mutlak tunggal:

Ri=Yiμ^i(Xi)R_i = |Y_i - \hat{\mu}_{-i}(X_i)|

Selang ramalan jackknife+ kemudiannya dibina daripada ramalan LOO pada titik ujian, dilebarkan oleh residual ini:

C(Xn+1)=[qα ⁣{μ^i(Xn+1)Ri},    q1α+ ⁣{μ^i(Xn+1)+Ri}]C(X_{n+1}) = \left[\, q_{\alpha}^{-}\!\left\{\hat{\mu}_{-i}(X_{n+1}) - R_i\right\}, \;\; q_{1-\alpha}^{+}\!\left\{\hat{\mu}_{-i}(X_{n+1}) + R_i\right\} \right]

Di sini q1α+{vi}q_{1-\alpha}^{+}\{v_i\} melambangkan nilai terkecil ke-(1α)(n+1)\lceil (1-\alpha)(n+1)\rceil bagi set {vi}i=1n\{v_i\}_{i=1}^n, dan qα{vi}q_{\alpha}^{-}\{v_i\} pula nilai terkecil ke-α(n+1)\lfloor \alpha(n+1)\rfloor. Batas bawah menolak residual daripada setiap ramalan LOO; batas atas menambah residual itu. Ketaksimetrian itulah intipatinya — meruntuhkan kedua-dua batas kepada μ^i+Ri\hat{\mu}_{-i} + R_i akan meletakkan batas bawah di atas ramalan, yang merupakan satu kesilapan.

Jackknife+ memberikan jaminan liputan sekurang-kurangnya 12α1 - 2\alpha (sedikit lebih lemah berbanding 1α1-\alpha split conformal), tetapi ia menggunakan kesemua data untuk kedua-dua latihan dan kalibrasi. Dalam amalan, liputan biasanya hampir dengan 1α1-\alpha.

Untuk model dagangan yang dilatih pada data terhad (cth. model khusus rejim dengan hanya beberapa ratus pemerhatian), jackknife+ selalunya merupakan pilihan terbaik — ia tidak mengorbankan data yang langka untuk kalibrasi. Kosnya ialah nn suaian semula model.

Masalah dengan Siri Masa Kewangan: Bukan Kebolehtukaran

Bukan kebolehtukaran dalam siri masa kewangan: siri tak pegun dengan anjakan rejim, liputan retak apabila taburan hanyut

Jaminan conformal piawai memerlukan kebolehtukaran: taburan tercantum bagi (Z1,,Zn+1)(Z_1, \ldots, Z_{n+1}) adalah tak berubah di bawah pilih atur. Untuk data i.i.d., ini terpenuhi secara remeh.

Siri masa kewangan bukanlah boleh ditukar. Pulangan mempamerkan:

  • Pengelompokan volatiliti: Tempoh volatiliti tinggi mengikuti tempoh volatiliti tinggi (kesan GARCH).
  • Momentum dan pulangan kepada min: Autokorelasi dalam pulangan atau pulangan kuasa dua.
  • Perubahan rejim: Taburan beralih dari semasa ke semasa (pasaran menaik vs. pasaran menurun).

Jika anda secara naif menerapkan split conformal pada siri masa — menggunakan pemecahan kalibrasi rawak — anda melanggar struktur temporal. Skor kalibrasi daripada tahun 2017 yang tenang tidak akan mencerminkan ketidakpastian tahun 2020 yang bergelora. Jaminan liputan anda terbongkar.

Adaptive Conformal Inference (ACI)

Adaptive conformal inference: selang ramalan yang melebar dan menyempit melalui gelung maklum balas yang menjejaki liputan teralami ke arah sasaran

Gibbs dan Candes (2021, NeurIPS) memperkenalkan Adaptive Conformal Inference (ACI) untuk menangani anjakan taburan dan data bukan-kebolehtukaran. Ideanya elegan: daripada menggunakan paras liputan tetap, sesuaikan paras kesilapan liputan sasaran secara dalam talian berdasarkan sama ada selang terkini meliputi keputusan sebenar, dan terbitkan semula kuantil daripada taburan skor pada setiap langkah.

Algoritma ACI

ACI tidak menolak lebar selang secara langsung. Ia mengekalkan parameter kesilapan liputan adaptif αt\alpha_t dan mengira semula kuantil conformal daripadanya. Pada setiap langkah masa tt:

  1. Kira ambang conformal sebagai kuantil empirik (1αt)(1 - \alpha_t) bagi set residual semasa (skor kalibrasi, ditambah sebarang skor teralami setakat ini): q^t=Quantile^1αt({Rj})\hat{q}_t = \widehat{\text{Quantile}}_{1-\alpha_t}(\{R_j\}).
  2. Perhatikan ciri XtX_t, hasilkan selang Ct(Xt)=[μ^(Xt)q^t,  μ^(Xt)+q^t]C_t(X_t) = [\hat{\mu}(X_t) - \hat{q}_t, \; \hat{\mu}(X_t) + \hat{q}_t].
  3. Perhatikan nilai sebenar YtY_t dan kira penunjuk ralat errt=1{YtCt(Xt)}\text{err}_t = \mathbf{1}\{Y_t \notin C_t(X_t)\}.
  4. Kemas kini paras (bukan lebar):

αt+1=clip ⁣(αt+γ(αerrt),  0,  1)\alpha_{t+1} = \text{clip}\!\left(\alpha_t + \gamma\,(\alpha - \text{err}_t),\; 0,\; 1\right)

di mana γ>0\gamma > 0 ialah saiz langkah dan α\alpha ialah kesilapan liputan sasaran. Jika satu selang tersasar (errt=1\text{err}_t = 1), αt\alpha_t mengecut, yang menolak kuantil seterusnya lebih tinggi dan melebarkan selang; jika ia meliputi, αt\alpha_t membesar dan selang mengetat. Yang pentingnya, γ\gamma di sini berada dalam unit kebarangkalian — ia menolak satu paras dalam [0,1][0,1], bukan ambang dalam unit pulangan mentah — jadi γ\gamma yang sama berkelakuan munasabah sama ada residual berada pada susunan 10310^{-3} atau tidak.

Jaminan Liputan untuk ACI

ACI memberikan jaminan liputan jangka panjang yang tidak bergantung pada model taburan:

1Tt=1TerrtααT+1α1γT\left|\frac{1}{T}\sum_{t=1}^{T} \text{err}_t - \alpha\right| \leq \frac{|\alpha_{T+1} - \alpha_1|}{\gamma T}

Oleh sebab αt\alpha_t diapit kepada [0,1][0,1], pengangka terbatas oleh 1/γ1/\gamma darab suatu pemalar, jadi sebelah kanan ialah O(1/T)O(1/T) dan kekerapan kesilapan liputan empirik menumpu kepada α\alpha. Kenyataan tepatnya: ACI menjamin kekerapan kesilapan liputan empirik jangka panjang menumpu kepada α\alpha untuk jujukan sebarangan (termasuk yang bermusuhan), dengan syarat paras tersuai kekal terbatas — yang ditegakkan oleh pengapitan. Ia merupakan jaminan terhadap kekerapan liputan, bukan terhadap keinformatifan selang: di bawah jujukan yang benar-benar bermusuhan, selang boleh membesar menjadi lebar tanpa maklumat sambil masih mencapai sasaran liputan.

Dynamically-Tuned ACI (DtACI)

Gibbs dan Candes (2024, JMLR) memperhalusi ACI dengan penalaan dinamik saiz langkah γ\gamma. Daripada menetapkan γ\gamma, mereka mengekalkan set calon Γ={γ1,,γK}\Gamma = \{\gamma_1, \ldots, \gamma_K\} dan menggabungkannya melalui peraturan agregasi pakar, mengutamakan γ\gamma yang liputan terkininya paling hampir dengan sasaran.

Ini menangani satu masalah praktikal: γ\gamma yang besar menyesuaikan dengan cepat kepada perubahan rejim tetapi menghasilkan lebar selang yang tidak stabil; γ\gamma yang kecil adalah stabil tetapi lambat menyesuaikan. DtACI mengimbangi kedua-duanya secara automatik.

Mengapa Ini Penting untuk Dagangan

Pertimbangkan satu strategi pembuatan pasaran (market-making) yang menggunakan model ramalan pulangan. Semasa pasaran tenang, selang conformal adalah ketat — model yakin, dan anda boleh mengambil posisi yang lebih besar. Apabila volatiliti melonjak (musim pendapatan, pengumuman FOMC, kejutan geopolitik), paras ACI menyesuaikan dan selang melebar dalam beberapa langkah masa. Penentuan saiz posisi anda mengecut sebagai tindak balas, tanpa sebarang model volatiliti eksplisit atau logik pengesanan rejim.

Inilah kuantifikasi ketidakpastian sebagai isyarat kelas pertama, bukan renungan kemudian.

Penentuan Saiz Posisi dengan Selang Conformal

Memetakan selang ketidakpastian terkalibrasi kepada saiz posisi: selang ketat memacu posisi besar, selang lebar memacu posisi kecil

Kini mari kita hubungkan conformal prediction dengan penentuan saiz posisi yang konkrit. Pemboleh ubah utama ialah separuh-lebar selang ramalan berbanding kes residual-mutlak simetri. Dengan selang simetri [μ^(Xt)q^t,  μ^(Xt)+q^t][\hat{\mu}(X_t) - \hat{q}_t, \; \hat{\mu}(X_t) + \hat{q}_t], lebar penuhnya ialah wt=2q^tw_t = 2\hat{q}_t. Untuk memastikan formula dan kod konsisten, kita mengukur segala-galanya terhadap lebar penuh wtw_t di sepanjang catatan ini.

Penentuan Saiz Songsang-Lebar

Pendekatan paling ringkas: tentukan saiz berkadar songsang dengan lebar selang.

position_sizet=kwt\text{position\_size}_t = \frac{k}{w_t}

di mana kk ialah pemalar penskalaan yang dikalibrasi kepada belanjawan risiko anda. Apabila model yakin (selang sempit), anda mengambil posisi yang lebih besar. Apabila tidak pasti (selang lebar), anda mengambil posisi yang lebih kecil.

Ini analog dengan penyasaran volatiliti (size1/σ\text{size} \propto 1/\sigma), tetapi dengan perbezaan penting: lebar selang conformal ialah ukuran ketidakpastian bebas-taburan, bukan anggaran volatiliti parametrik. Ia menangkap ketidakpastian ramalan di bawah jaminan liputan, bukan sekadar varians pulangan.

Penentuan Saiz Nisbah-Kelebihan dan Penapis Tanpa-Dagang

Penentuan saiz songsang-lebar tulen mengabaikan kekuatan isyarat itu sendiri. Penghalusan yang semula jadi menskalakan dengan nisbah kelebihan (edge ratio) — ramalan titik berbanding lebar selang:

et=μ^(Xt)wte_t = \frac{|\hat{\mu}(X_t)|}{w_t}

Ini ialah analog conformal bagi nisbah isyarat-ke-hingar: pulangan jangkaan dibahagi dengan ukuran ketidakpastian bebas-taburan. Kita menggunakannya untuk kedua-dua penentuan saiz dan untuk penapis tanpa-dagang.

Penapis itu berprinsip. Jika selang menongkah sifar,

lowert<0<uppert,\text{lower}_t < 0 < \text{upper}_t,

maka selang ramalan (1α)(1-\alpha) merangkumi kedua-dua pulangan positif dan negatif — pulangan teralami boleh secara munasabah mempunyai tanda yang bertentangan dengan ramalan anda. Takrifkan ambang kelebihan minimum θ\theta dan berdagang hanya apabila et>θe_t > \theta. Perhatikan bahawa kandungan geometri "ete_t cukup besar sehingga selang tidak lagi menongkah sifar" adalah tepatnya et>1/2e_t > 1/2 (kerana selang melepasi sifar apabila μ^>q^t=wt/2|\hat{\mu}| > \hat{q}_t = w_t/2). Pilih θ\theta pada skala sebenar et=μ^/wte_t = |\hat{\mu}|/w_t melalui backtesting; untuk residual pulangan harian, ete_t biasanya jauh di bawah 1/21/2, jadi θ\theta yang kecil mungkin membenarkan hampir semua dagangan dan θ\theta yang besar mungkin tidak membenarkan satu pun. Kalibrasikannya kepada data anda.

Tentang "Conformal Kelly"

Adalah menggoda untuk menggandingkan selang conformal kepada pecahan Kelly f=pb(1p)bf^* = \frac{pb - (1-p)}{b}. Tetapi ff^* sudah pun merupakan pecahan lengkap dan terbatas yang diterbitkan daripada kebarangkalian menang pp dan nisbah ganjaran bb; mendarabkannya dengan nisbah tak terbatas seperti μ^/q^\hat{\mu}/\hat{q} tidak mempunyai justifikasi teori-keputusan — ia boleh melebihi 1 atau membalikkan tanda secara bebas daripada ff^*, dan ia mengira dua kali kelebihan yang telah pun dikodkan oleh ff^*. Maka kami tidak mempersembahkan pendarab "conformal Kelly".

Jika anda ingin memacu Kelly daripada selang itu, anda mesti benar-benar menerbitkan pp dan bb daripadanya, yang memerlukan andaian eksplisit tentang taburan dalam selang (selang conformal sengaja tidak mengatakan apa-apa tentang itu — lihat Batasan). Sebagai contoh, di bawah bentuk dalam-selang yang diandaikan anda boleh menghampiri pP(return>0)p \approx \mathbb{P}(\text{return} > 0) dan nisbah ganjaran daripada geometri selang — tetapi tonjolkan andaian itu dengan lantang, kerana ia memperkenalkan semula tepatnya komitmen parametrik yang sepatutnya dielakkan oleh conformal prediction.

Alternatif yang jujur dan ringan-andaian ialah menggunakan nisbah kelebihan et=μ^/wte_t = |\hat{\mu}|/w_t sebagai pengecutan pecahan-Kelly: besarkan saiz apabila pulangan jangkaan besar berbanding selang, kecilkan saiz apabila ia kecil, dan terapkan ini di atas had keras — secara eksplisit sebagai heuristik, bukan sebagai "pecahan Kelly".

Pelaksanaan Python

Berikut ialah pelaksanaan praktikal. Kami menunjukkan kedua-dua laluan split/prefit dan laluan temporal (EnbPI), kerana inti catatan ini ialah bahawa data kewangan bukanlah boleh ditukar.

Penyediaan dan Persediaan 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" (penganggar split-conformal naif). Pilihan method="plus" ialah penganggar CV+/jackknife+ dan tidak serasi dengan cv="prefit" — ia memerlukan objek pengesahan silang sebaliknya. Kami menggunakan gabungan yang betul di sini, dan menyeragamkan penentuan saiz 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 Siri Masa dengan EnbPI

Oleh sebab pulangan bukanlah boleh ditukar, pemecahan rawak/prefit di atas hanyalah satu garis dasar. MapieTimeSeriesRegressor daripada MAPIE dengan method="enbpi" (Xu & Xie, 2021) menggunakan block bootstrap dan pengemaskinian residual yang direka untuk kebergantungan temporal. Inilah alat yang sepadan dengan hujah catatan 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 (Dalam Talian)

Untuk dagangan langsung kami melaksanakan ACI sebenar dari awal: kekalkan paras kesilapan liputan αt\alpha_t, kemas kininya secara aditif, dan terbitkan semula kuantil daripada set residual pada setiap langkah. Dua butiran sampel terhingga penting:

  • Gunakan statistik tertib (order statistic), bukan kuantil terinterpolasi. np.quantile melakukan interpolasi secara lalai, yang boleh jatuh sedikit di bawah pangkat yang diperlukan dan terkurang-liputan; hantar method="higher" (setara dengan "inverted_cdf").
  • Apabila pangkat yang diperlukan melebihi nn (nn kecil, liputan sasaran tinggi), ambang yang betul ialah ++\infty (selang = keseluruhan garis), bukan apitan kepada residual terbesar. Mengapit secara senyap mematahkan jaminan 1αt\ge 1-\alpha_t.
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))

Menyatukannya: Gelung 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

Menilai Keputusan

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 Praktikal

Memilih Skor Nonconformity

Residual mutlak Yμ^(X)|Y - \hat{\mu}(X)| ialah pilihan lalai, tetapi ia mengandaikan selang ramalan sepatutnya simetri di sekeliling ramalan titik. Untuk pulangan kewangan, selang tak simetri selalunya lebih masuk akal:

  • Conformalized Quantile Regression (CQR): Suaikan peregres kuantil pada paras α/2\alpha/2 dan 1α/21 - \alpha/2, kemudian conformalize (Romano et al., 2019). Selang itu menyesuaikan bentuknya kepada taburan setempat — lebih lebar di sebelah bawah semasa drawdown, lebih lebar di sebelah atas semasa lonjakan. (Dengan CQR selang itu tidak lagi simetri, jadi wtw_t ialah lebar atas-tolak-bawah yang tulen — terus gunakan wtw_t sebagai penyebut di mana-mana sahaja.)
  • Skor ternormal: Ri=Yiμ^(Xi)/σ^(Xi)R_i = |Y_i - \hat{\mu}(X_i)| / \hat{\sigma}(X_i), di mana σ^\hat{\sigma} ialah anggaran volatiliti setempat. Ini menghasilkan selang yang menyesuaikan secara bersyarat — lebih ketat semasa rejim volatiliti rendah, lebih lebar semasa rejim volatiliti tinggi — sambil mengekalkan liputan marginal.

Saiz Set Kalibrasi

Jaminan liputan split conformal sah untuk sebarang saiz set kalibrasi nn, tetapi lebar selang berkurangan apabila nn meningkat. Untuk nn yang sangat kecil, statistik tertib yang diperlukan mungkin tidak wujud, dalam keadaan itu ambang yang jujur ialah ++\infty (selang yang tidak informatif tetapi sah) — mengapit kepada residual terbesar secara senyap membatalkan jaminan. Untuk dagangan praktikal:

  • n100n \geq 100 titik kalibrasi memberikan selang yang agak ketat.
  • n500n \geq 500 lebih disukai untuk anggaran kuantil yang stabil.
  • Dengan ACI, set kalibrasi hanya untuk pemulaan; pengemaskinian paras dalam talian menguruskan selebihnya.

Kekerapan Latihan Semula

Model asas μ^\hat{\mu} boleh menjadi basi. Dua pendekatan:

  1. Latih semula secara berkala (cth. bulanan) dan kalibrasi semula kuantil conformal.
  2. Gunakan ACI dan biarkan paras adaptif mengimbangi kebasian model — selang melebar secara automatik apabila residual model membesar.

Pilihan 2 lebih ringkas dan mengejutkan keberkesanannya. Lapisan conformal bertindak sebagai jaring keselamatan: walaupun model merosot, kekerapan liputan ACI jangka panjang masih menumpu kepada sasaran.

Kos Transaksi

Selang conformal berinteraksi dengan kos transaksi dengan cara yang berguna. Apabila selang lebar (ketidakpastian tinggi), posisi adalah kecil, jadi pusing ganti (turnover) adalah rendah. Apabila selang menyempit (model yakin), posisi membesar — tetapi model lebih berkemungkinan betul, jadi pusing ganti itu berbaloi untuk dibayar.

Anda juga boleh menggabungkan kos transaksi secara langsung ke dalam penapis tanpa-dagang:

trade only if μ^(Xt)cost>θwt\text{trade only if } |\hat{\mu}(X_t)| - \text{cost} > \theta \cdot w_t

Ini memastikan anda hanya berdagang apabila kelebihan bersih jangkaan melebihi satu ambang yang diskalakan oleh lebar conformal — menggunakan penyebut wtw_t yang sama seperti di mana-mana sahaja.

Perbandingan dengan Kaedah Tradisional

Sifat Gaussian CI Bootstrap CI Conformal PI
Andaian taburan Ralat normal i.i.d. + asimptot Tiada (kebolehtukaran)
Jaminan sampel terhingga Tidak Tidak Ya
Berfungsi dengan sebarang model Tidak Ya Ya
Menyesuaikan dengan heteroskedastisiti Tidak Sebahagian Dengan CQR / skor ternormal
Mengendalikan anjakan taburan Tidak Tidak Varian ACI / EnbPI
Kos pengkomputeran Rendah Sederhana Split: rendah; jackknife+: O(n)O(n) suaian semula; full: terlalu mahal

Bootstrap adalah "asimptot" hanya dalam jaminannya; ia masih mengandaikan data i.i.d./boleh ditukar dan kelicinan, jadi sel andaian-taburannya bukanlah "bebas-andaian". Dan lajur tunggal "conformal" itu menyembunyikan kos yang sangat berbeza merentas varian, yang kini dijelaskan oleh baris kos.

Batasan

Conformal prediction bukanlah sihir. Batasan utama untuk dagangan:

  1. Liputan marginal, bukan bersyarat. Jaminannya ialah P(Yn+1C(Xn+1))1α\mathbb{P}(Y_{n+1} \in C(X_{n+1})) \geq 1 - \alpha secara marginal — dipuratakan ke atas kerawakan dalam kedua-dua XX dan YY. Untuk nilai pensyaratan tertentu X=xX = x, liputan setempat boleh berada di atas atau di bawah 1α1 - \alpha. Conformalized quantile regression menangani perkara ini sebahagiannya.

  2. Kebolehtukaran ialah syarat sebenar. Untuk split conformal, data kalibrasi dan titik ujian mesti boleh ditukar. Data kewangan tidak. ACI dan EnbPI melonggarkan ini kepada jaminan jangka panjang, tetapi liputan jangka pendek boleh menyimpang.

  3. Lebar selang bukan ketumpatan kebarangkalian. Selang conformal memberitahu anda di mana YY akan jatuh dengan kebarangkalian 1α1-\alpha, tetapi ia tidak mengatakan apa-apa tentang taburan dalam selang itu. Ia bukan pengganti bagi taburan ramalan penuh — yang merupakan tepatnya sebab menerbitkan pp dan bb Kelly daripadanya memerlukan andaian tambahan yang eksplisit.

  4. Sampah masuk, lebih lebar keluar. Model asas yang buruk menghasilkan selang yang lebar. Conformal prediction menjamin liputan tetapi bukan kegunaan. Jika model anda tiada kuasa ramalan, selang akan menjadi begitu lebar sehingga penentu saiz posisi tidak pernah berdagang.

Ringkasan

Conformal prediction menyediakan rangka kerja yang teliti dan bebas-taburan untuk kuantifikasi ketidakpastian yang menyelit secara semula jadi ke dalam penentuan saiz posisi:

  • Split conformal untuk kalibrasi statik luar talian dengan liputan sampel terhingga.
  • Jackknife+ apabila data kalibrasi langka dan anda ingin menggunakan kesemua pemerhatian (dengan kos nn suaian semula).
  • Adaptive conformal inference / EnbPI untuk dagangan dalam talian dengan pasaran tak pegun.
  • Penentuan saiz posisi melalui lebar selang songsang dan penapis tanpa-dagang nisbah-kelebihan — dan, jika anda pergi ke Kelly, hanya selepas menerbitkan pp dan bb secara jujur, bukan melalui pendarab yang tidak berjustifikasi.

Kelebihan utama berbanding alternatif parametrik: anda tidak pernah perlu menentukan atau mengesahkan andaian taburan parametrik. Selang itu jujur secara binaan (secara marginal, di bawah kebolehtukaran). Bagi seorang pedagang sistematik, ini bermaksud satu lagi sumber risiko model yang berkurang — dan dalam perniagaan yang risiko modelnya bersifat eksistensial, itu penting.


Rujukan:

  • 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.
Penafian: Maklumat yang disediakan dalam artikel ini adalah untuk tujuan pendidikan dan maklumat sahaja dan bukan merupakan nasihat kewangan, pelaburan, atau dagangan. Dagangan mata wang kripto melibatkan risiko kerugian yang ketara.

Pengarang

Eugen Soloviov
Eugen Soloviov

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.

Newsletter

Kekal Mendahului Pasaran

Langgan surat berita kami untuk pandangan dagangan AI eksklusif, analisis pasaran, dan kemas kini platform.

Kami menghormati privasi anda. Berhenti melanggan pada bila-bila masa.