← Kembali ke artikel
May 25, 2026
Bacaan 5 minit

Di Dalam Algoritma Dalaman Kami: HRP + Long/Short + CVaR dengan Hull-White

Di Dalam Algoritma Dalaman Kami: HRP + Long/Short + CVaR dengan Hull-White
#pengoptimuman portfolio
#HRP
#hierarchical risk parity
#CVaR
#Hull-White
#EWMA
#long/short
#pengurusan risiko
#Rust
#kewangan kuantitatif

Dalam ulasan kami «12 Algoritma Pengoptimuman Portfolio, Dibandingkan» kami menguji selusin kaedah peruntukan secara serentak. Sebelas daripadanya adalah klasik buku teks. Yang kedua belas, Pipeline, adalah milik kami — dan ia hanya mendapat satu poin ringkas dalam artikel tersebut. Artikel ini adalah penelitian mendalam: apa yang ada di dalamnya, dari mana setiap formula berasal, dan bagaimana spesifikasi bertukar menjadi Rust.

Pipeline tidak mencipta cara baharu untuk mengira wajaran. Ia mengambil resipi paling kukuh yang diketahui — Hierarchical Risk Parity (HRP) — dan membungkusnya dalam dua lapisan yang sebenarnya diperlukan oleh akaun dagangan langsung tetapi HRP biasa tidak mempunyainya: arah (long/short daripada isyarat strategi) dan had belanjawan risiko yang keras (CVaR diselaraskan untuk rejim volatiliti semasa). Ini menghasilkan empat peringkat.

Empat peringkat

harga  I  pulangan log  II  wajaran HRP  III  long/short  IV  belanjawan CVaR\text{harga} \;\xrightarrow{\text{I}}\; \text{pulangan log} \;\xrightarrow{\text{II}}\; \text{wajaran HRP} \;\xrightarrow{\text{III}}\; \text{long/short} \;\xrightarrow{\text{IV}}\; \text{belanjawan CVaR}
  • I — pulangan log setiap aset.
  • II — wajaran asas daripada HRP.
  • III — pembahagian long/short daripada isyarat agen, dengan bahagian risiko ditetapkan mengikut keyakinan.
  • IV — pembetulan CVaR dengan volatiliti Hull-White; lebihan risiko dipindahkan ke tunai.

Mari kita lalui satu persatu mengikut urutan.

Peringkat I. Pulangan log

Segalanya bermula dengan peralihan daripada harga kepada pulangan log:

ri,t=ln ⁣(Si,tSi,t1)r_{i,t} = \ln\!\left(\frac{S_{i,t}}{S_{i,t-1}}\right)

di mana ii ialah aset dan tt ialah langkah masa. Pulangan log terkumpul dari masa ke masa dan lebih simetri berbanding perubahan peratusan biasa — input standard untuk sebarang matematik kovarian.

Peringkat II. HRP sebagai asas

HRP, yang dicadangkan oleh Marcos López de Prado pada tahun 2016, mengelakkan penyakit utama Mean-Variance Optimization — pembalikan matriks kovarian yang bersyarat buruk. Ia tidak pernah membalikkannya langsung. Sebaliknya, ia bekerja dengan struktur korelasi.

Kovarian dan korelasi

Daripada pulangan, kami membina matriks kovarian Σ\Sigma dan menormalisasikannya kepada matriks korelasi CC:

Σi,j=Cov(ri,rj),Ci,j=ρi,j=Cov(ri,rj)σiσj\Sigma_{i,j} = \mathrm{Cov}(r_i, r_j), \qquad C_{i,j} = \rho_{i,j} = \frac{\mathrm{Cov}(r_i, r_j)}{\sigma_i \sigma_j}

Matriks jarak

Kami menukar korelasi kepada metrik jarak, supaya aset yang berkorelasi kuat berada "berdekatan":

di,j=1ρi,j2d_{i,j} = \sqrt{\frac{1 - \rho_{i,j}}{2}}

Semakin dekat ρi,j\rho_{i,j} kepada 1, semakin dekat di,jd_{i,j} kepada 0 — dan semakin besar kemungkinan aset tersebut berkongsi kluster.

Dendrogram dan tertib daun

Daripada matriks jarak, kami membina hierarki kluster melalui average linkage dan membaca tertib daun π=(π1,,πN)\pi = (\pi_1, \ldots, \pi_N) — satu permutasi aset di mana aset yang serupa berada bersebelahan.

Langkah pilihan: bilangan kluster yang optimum boleh dipilih melalui pekali silhouette si=biaimax(bi,ai)s_i = \dfrac{b_i - a_i}{\max(b_i, a_i)}, di mana aia_i ialah jarak min dalam kluster dan bib_i ialah jarak min ke kluster jiran terdekat. Laluan asas tidak memerlukannya — biseksi rekursif sudah menghormati hierarki.

Kuasi-pepenjuruan

Kami menukar baris dan lajur Σ\Sigma mengikut π\pi, mengumpulkan nilai besar sepanjang pepenjuru:

Σi,jq=Σπi,πj\Sigma^{q}_{i,j} = \Sigma_{\pi_i, \pi_j}

Biseksi rekursif

Kemudian rekursi berjalan dari atas ke bawah. Pada setiap langkah, sebuah kluster dibahagi dua kepada LL dan RR, dan modal diagihkan antara kedua-dua bahagian secara songsang berkadar dengan varians mereka:

wL=1/σL21σL2+1σR2,wR=1wLw_L = \frac{1/\sigma_L^2}{\dfrac{1}{\sigma_L^2} + \dfrac{1}{\sigma_R^2}}, \qquad w_R = 1 - w_L

Varians kluster dikira pada sub-blok kovariannya sebagai σC2=1m2i,jCΣi,jq\sigma_C^2 = \tfrac{1}{m^2}\sum_{i,j \in C}\Sigma^{q}_{i,j}. Penurunan diteruskan sehingga setiap nod memegang satu aset sahaja. Wajaran adalah long-only, tidak negatif, dan berjumlah 1.0.

Dalam implementasi kami, ini adalah fungsi hrp_from_cov(cov) -> Vec<f64>: korelasi → jarak → average linkage → tertib daun → kuasi-pepenjuruan → biseksi rekursif. Pipeline memanggilnya sebagai asas — dan ia juga merupakan optimize() awam untuk kes tanpa isyarat.

Peringkat III. Lapisan long/short

HRP biasa adalah portfolio "beli sahaja". Namun, strategi sering mengatakan bukan sahaja berapa banyak tetapi ke arah mana. Peringkat III mengambil isyarat per-aset (Long/Short) daripada agen dan membina dua sub-portfolio.

  1. Aset dibahagikan kepada bakul long dan short mengikut isyarat.
  2. Di dalam setiap bakul, wajaran dikira dengan HRP yang sama (pada sub-blok kovarian aset tersebut), berjumlah 1 per bakul.
  3. Jika agen juga mengeluarkan keyakinan pip_i, bahagian risiko antara kedua-dua pihak ditetapkan oleh jumlah keyakinan:

ξL=iLpi,ξS=iSpi,λL=ξLξL+ξS,λS=ξSξL+ξS\xi_L = \sum_{i \in L} p_i, \quad \xi_S = \sum_{i \in S} p_i, \qquad \lambda_L = \frac{\xi_L}{\xi_L + \xi_S}, \quad \lambda_S = \frac{\xi_S}{\xi_L + \xi_S}

Tanpa keyakinan, bahagian akan kembali kepada bilangan aset dalam setiap bakul. Wajaran bertanda akhir ialah wi=λLwiHRPw_i = \lambda_L \cdot w_i^{\text{HRP}} untuk long dan wi=λSwiHRPw_i = -\lambda_S \cdot w_i^{\text{HRP}} untuk short, selepas itu keseluruhan pendedahan kasar dinormalisasi kepada 1.

Nota jujur tentang kod. Spesifikasi asal membawa faktor pembetulan αL=λL/σL\alpha_L = \sqrt{\lambda_L}/\sigma_L, αS=λS/σS\alpha_S = \sqrt{\lambda_S}/\sigma_S — tetapi ia juga menandakannya dengan "adakah kita perlukan langkah ini?". Implementasi tidak menerapkannya: kedua-dua pihak digabungkan secara langsung melalui bahagian risiko λ\lambda, yang mengekalkan pendedahan kasar tepat pada 1 dan tidak mewujudkan leverage tersembunyi. Ini adalah penyederhanaan spesifikasi yang disengajakan, bukan kesilapan.

Peringkat IV. CVaR dengan pelarasan Hull-White

HRP mengimbangi risiko secara struktur, tetapi tidak mengetahui apa-apa tentang tahap risiko mutlak dalam nilai wang. Peringkat akhir meletakkan had keras pada risiko ekor — dan menjadikannya sensitif terhadap perubahan rejim pasaran.

Pulangan portfolio dan volatiliti EWMA

Pertama, kami menggabungkan wajaran kepada pulangan portfolio dan menganggar volatiliti bersyarat dengan EWMA:

rp,t=i=1nwiri,t,σp,t2=λσp,t12+(1λ)rp,t12r_{p,t} = \sum_{i=1}^{n} w_i\, r_{i,t}, \qquad \sigma_{p,t}^2 = \lambda\, \sigma_{p,t-1}^2 + (1 - \lambda)\, r_{p,t-1}^2

dengan λ=0.94\lambda = 0.94 (nilai RiskMetrics klasik). EWMA memberikan volatiliti "hari ini" dan bukannya purata sepanjang sejarah.

Penskalaan semula Hull-White

Idea utama: pulangan lalu tidak boleh diambil apa adanya — ia berlaku di bawah volatiliti yang berbeza. Kaedah Hull-White menskala semula setiap pulangan lalu kepada tahap semasa:

r~p,s=σp,t+1σp,srp,s,s=tN+1,,t\widetilde{r}_{p,s} = \frac{\sigma_{p,t+1}}{\sigma_{p,s}}\, r_{p,s}, \qquad s = t - N + 1, \ldots, t

Bulan yang tenang "diregangkan", bulan yang bergolak "dimampatkan", dan taburan dibawa ke dalam rejim semasa.

VaR dan CVaR

Pada taburan yang diskala semula, kami mengambil kuantil kerugian dan purata kerugian dalam ekor:

VaRαHW=q1α(r~p)VaR_\alpha^{HW} = -q_{1-\alpha}(\widetilde{r}_p)

CVaRαHW=E ⁣[r~pr~pq1α(r~p)]CVaR_\alpha^{HW} = -\mathbb{E}\!\left[\widetilde{r}_p \mid \widetilde{r}_p \le q_{1-\alpha}(\widetilde{r}_p)\right]

CVaR (juga dikenali sebagai Expected Shortfall) menjawab bukan "seberapa buruk hari buruk yang biasa" tetapi "seberapa buruk ia secara purata merentasi α\alpha peratus terburuk" — jadi ia melihat ketebalan ekor, bukan sekadar tepinya.

Belanjawan risiko dan tunai

Jika CVaR melebihi ambang yang boleh diterima, setiap kedudukan berisiko dikecilkan oleh satu faktor tunggal, dan modal yang dibebaskan dipindahkan ke tunai:

winew=γwi,γ=CVaRmaxCVaRαHW,wcash=1i=1nwineww_i^{new} = \gamma\, w_i, \quad \gamma = \frac{CVaR_{\max}}{CVaR_\alpha^{HW}}, \qquad w_{cash} = 1 - \sum_{i=1}^{n} |w_i^{new}|

Jadi portfolio menyahrisiko dirinya sendiri apabila risiko ekor meningkat dan memasuki semula pasaran apabila ia tenang.

Daripada spesifikasi kepada kod

Keseluruhan algoritma berada dalam satu crate Rust, portfolio-pipeline, dan mematuhi kontrak seragam workspace:

pub fn optimize(prices: &[Vec<f64>]) -> Vec<f64>

Ini adalah unjuran long-only (peringkat I, II, IV tanpa isyarat) — antara muka prices -> weights yang sama persis seperti sebelas algoritma lain, jadi Pipeline adalah pengganti langsung untuk mana-mana daripadanya. Versi penuh dengan setiap peringkat adalah fungsi berasingan:

pub fn run(
    prices: &[Vec<f64>],
    signals: Option<&[Side]>,      // Long / Short per asset
    confidence: Option<&[f64]>,    // agent confidence → risk shares λ
    cfg: &PipelineConfig,          // CVaR / Hull-White parameters
) -> PipelineResult                // signed weights + cash + cvar + σ

Lalai lapisan: ekor cvar_alpha = 0.05, belanjawan cvar_max = 0.05, EWMA ewma_lambda = 0.94, tetingkap Hull-White hw_window = 0 (semua sejarah). Implementasi tidak mempunyai kebergantungan luaran dan sengaja bersifat defensif: pada sejarah pendek (kurang daripada 4 titik harga) ia mengembalikan wajaran sama rata, dan lapisan CVaR hanya diaktifkan pada ≥8 pemerhatian pulangan — jika tidak, tidak ada apa-apa untuk menganggar ekor.

Mengapa Rust: satu pangkalan kod deterministik untuk kedua-dua backtest dan pengeluaran, tanpa penyimpangan "Python dalam penyelidikan, sesuatu yang lain dalam prod", dan cukup pantas untuk menjalankan semua dua belas algoritma dalam satu permintaan melalui backend perbandingan.

Kos dari segi masa

Seberapa pantas "cukup pantas"? Kami mengekstrak teras HRP (pulangan log → kovarian → average linkage → kuasi-pepenjuruan → wajaran rekursif) ke dalam penanda aras berdiri sendiri dan menjalankan matematik yang sama persis merentasi tujuh bahasa — C, C++, Rust, Zig, Python, Node.js dan Bun — di bawah syarat yang sama: Apple Silicon, satu thread, 365 pemerhatian harian per aset, harga sintetik, bilangan aset NN dari 10 hingga 10,000.

Satu kata tentang kerumitan, kerana ia menentukan keseluruhan bentuk. Versi buku teks average linkage mengimbas semula matriks jarak penuh untuk pasangan terdekat pada setiap gabungan — itu ialah O(N3)O(N^3) dan menjadi kesesakan pada beberapa ribu aset. Penanda aras sebaliknya menggunakan algoritma rantai jiran terdekat O(N2)O(N^2) (Müllner 2011) — yang sama di sebalik linkage(method='average') SciPy. Dengan itu, pengklusteran bukan lagi peringkat dominan: pada N=2000N = 2000 ia adalah ~15 ms daripada laluan ~0.5 s. Kos kini didominasi oleh matriks kovarian, O(N2T)O(N^2 \cdot T) — peringkat tunggal yang tidak dapat dielakkan oleh mana-mana kaedah bergaya HRP.

Apa yang ditunjukkan oleh larian (jadual per-bahasa penuh dan skrip reproduksi satu arahan terdapat dalam repositori projek):

  • Pada portfolio yang realistik, ia percuma. Bakul kripto mengandungi berpuluh-puluh aset, jarang melebihi seratus. Pada N100N \le 100, satu laluan HRP penuh adalah milisaat satu digit walaupun dalam Node dan mikrosaat dalam Rust/C. Pengiraan semula wajaran pada setiap detik bukan isu.
  • Rust berada dalam ~1.0–1.3× daripada C — order magnitud yang sama, kedua-duanya dikompil, dan pada dasarnya setara apabila NN mencapai ribuan. C sedikit lebih pantas pada aritmetik mentah, tetapi Rust memberikan kebolehramalan yang sama tanpa pengumpul sampah dan tanpa UB.
  • Ia berskala hingga ribuan aset. Dengan linkage O(N2)O(N^2), satu laluan penuh adalah ~0.5 s pada N=2000N = 2000 dan beberapa saat pada N=5000N = 5000 dalam bahasa yang dikompil; malah Node yang ditafsirkan melangkaui N=2000N = 2000 dalam bawah dua saat. Apa yang menetapkan siling sekarang ialah peringkat kovarian, bukan pengklusteran.

Pengambilan pragmatik: pada saiz portfolio kami, memilih Rust bukan tentang "mengalahkan C" (C sedikit lebih pantas di sini) — ia tentang satu pangkalan kod deterministik untuk penyelidikan dan pengeluaran, tanpa jeda GC dan bertahun-tahun ruang kepala prestasi. Penanda aras tujuh bahasa penuh, dengan keputusan dan skrip reproduksi, terbuka dalam repositori projek.

Kedudukan Pipeline di antara dua belas

Dalam perbandingan kami pada satu bakul (yang sengaja disediakan), Pipeline berkelakuan seperti HRP — kerana melalui titik masuk long-only optimize() ia adalah HRP dengan lapisan CVaR. Mesin arahnya hanya hidup apabila anda memberinya isyarat strategi. Itulah keseluruhan intipatinya: Pipeline bukan "pengoptimum lain untuk wajaran backtesting" tetapi lapisan pelaksanaan antara isyarat strategi dan pesanan sebenar — ia mengambil panggilan beli/jual anda, mengatur modal mengikut HRP dalam setiap pihak, mengimbangi pihak-pihak mengikut keyakinan, dan memotong risiko ekor kepada belanjawan yang ditetapkan.

Untuk konteks penuh — sebelas kaedah lain yang ada dan bagaimana ia berbeza — lihat ulasan, «12 Algoritma Pengoptimuman Portfolio, Dibandingkan». Dan anda boleh mencubanya secara langsung di portfolio-optimizer.marketmaker.cc.

Rujukan

  1. López de Prado, M. (2016). Building Diversified Portfolios that Outperform Out of Sample. The Journal of Portfolio Management.
  2. López de Prado, M. (2018). Advances in Financial Machine Learning. Wiley.
  3. Hull, J., & White, A. (1998). Incorporating Volatility Updating into the Historical Simulation Method for Value at Risk. Journal of Risk.
  4. Rockafellar, R. T., & Uryasev, S. (2000). Optimization of Conditional Value-at-Risk. Journal of Risk.
  5. RiskMetrics Group (1996). RiskMetrics — Technical Document. J.P. Morgan.
  6. Marketmaker.cc: marketmaker.cc

Petikan

@article{soloviov2026pipeline,
  author = {Soloviov, Eugen and Zhuravleva, Marina and Kiselev, Kirill},
  title = {Inside Our House Algorithm: HRP + Long/Short + CVaR with Hull-White Adjustment},
  year = {2026},
  url = {https://marketmaker.cc/ms/blog/post/portfolio-pipeline-hrp-cvar},
  description = {A deep dive into Pipeline, a composite portfolio allocation algorithm built on Hierarchical Risk Parity with a signal-driven long/short overlay and a Hull-White CVaR risk-budget correction, with the full specification and its Rust implementation.}
}
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.

Marina Zhuravleva
Marina Zhuravleva

Financial mathematics

Fifth-year student at Bauman Moscow State Technical University (Automatic Control Systems), specializing in financial mathematics. Background in calibrating stochastic-volatility (Heston) and local-volatility (Dupire) models, fair pricing of options including exotics via both Monte-Carlo and analytic formulas, hedging-error reduction, and exposure to LSV models.

Kirill Kiselev
Kirill Kiselev

Portfolio optimization

Fourth-year student at the Faculty of Mechanics and Mathematics, Novosibirsk State University (NSU); thesis on Heston-model calibration and delta-hedging within the same model. Works on portfolio optimization.

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.