← กลับไปยังบทความ
May 25, 2026
อ่าน 5 นาที

เบื้องหลังอัลกอริทึมของเรา: HRP + Long/Short + CVaR กับ Hull-White

เบื้องหลังอัลกอริทึมของเรา: HRP + Long/Short + CVaR กับ Hull-White
#การปรับพอร์ตโฟลิโอ
#HRP
#hierarchical risk parity
#CVaR
#Hull-White
#EWMA
#long/short
#การบริหารความเสี่ยง
#Rust
#quantitative finance

ในบทความภาพรวม «12 อัลกอริทึมการปรับพอร์ตโฟลิโอ เปรียบเทียบกัน» เราได้แข่งขันวิธีการจัดสรรสิบสองแบบเคียงกัน สิบเอ็ดแบบเป็นทฤษฎีคลาสสิก แบบที่สิบสอง คือ Pipeline — เป็นของเรา และมันได้รับเพียงหนึ่งบรรทัดในโพสต์นั้น บทความนี้คือการเจาะลึก: ข้างในมีอะไร แต่ละสูตรมาจากไหน และข้อกำหนดแปลงเป็น Rust ได้อย่างไร

Pipeline ไม่ได้คิดค้นวิธีใหม่ในการคำนวณน้ำหนัก แต่นำสูตรที่แข็งแกร่งที่สุดที่รู้จัก — Hierarchical Risk Parity (HRP) — มาห่อด้วยสองชั้นที่บัญชีซื้อขายจริงต้องการแต่ HRP ธรรมดาขาด: ทิศทาง (long/short จากสัญญาณกลยุทธ์) และ งบประมาณความเสี่ยงแบบเข้มงวด (CVaR ที่ปรับตามระบอบความผันผวนปัจจุบัน) ซึ่งให้สี่ขั้นตอน

สี่ขั้นตอน

prices  I  log returns  II  HRP weights  III  long/short  IV  CVaR budget\text{prices} \;\xrightarrow{\text{I}}\; \text{log returns} \;\xrightarrow{\text{II}}\; \text{HRP weights} \;\xrightarrow{\text{III}}\; \text{long/short} \;\xrightarrow{\text{IV}}\; \text{CVaR budget}
  • I — log returns ของสินทรัพย์ทุกตัว
  • II — น้ำหนักพื้นฐานจาก HRP
  • III — การแบ่ง long/short จากสัญญาณ agent โดยส่วนแบ่งความเสี่ยงกำหนดจากระดับความเชื่อมั่น
  • IV — การปรับ CVaR ด้วยความผันผวน Hull-White; ส่วนเกินความเสี่ยงไปที่เงินสด

มาทำความเข้าใจแต่ละขั้นตามลำดับ

ขั้นตอน I. Log returns

ทุกอย่างเริ่มต้นด้วยการเปลี่ยนจากราคาเป็น log returns:

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

โดยที่ ii คือสินทรัพย์และ tt คือช่วงเวลา Log returns สะสมตามเวลาและมีความสมมาตรมากกว่าการเปลี่ยนแปลงเปอร์เซ็นต์ธรรมดา — ซึ่งเป็น input มาตรฐานสำหรับคณิตศาสตร์ covariance ทุกประเภท

ขั้นตอน II. HRP เป็นรากฐาน

HRP ที่ Marcos López de Prado เสนอในปี 2016 หลีกเลี่ยงโรคหลักของ Mean-Variance Optimization — การ invert เมทริกซ์ covariance ที่มีเงื่อนไขไม่ดี โดยไม่ต้อง invert เลย แต่ทำงานกับ โครงสร้าง ของ correlations แทน

Covariance และ Correlation

จาก returns เราสร้างเมทริกซ์ covariance Σ\Sigma และนอร์มัลไลซ์เป็นเมทริกซ์ correlation 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}

Distance Matrix

เราแปลง correlation เป็น metric ระยะทาง เพื่อให้สินทรัพย์ที่มี correlation สูงอยู่ "ใกล้กัน":

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

ยิ่ง ρi,j\rho_{i,j} ใกล้ 1 มากเท่าไร di,jd_{i,j} ก็ยิ่งใกล้ 0 — และสินทรัพย์มีโอกาสอยู่ใน cluster เดียวกันมากขึ้น

Dendrogram และลำดับใบ (Leaf Order)

จาก distance matrix เราสร้างลำดับชั้น cluster ผ่าน average linkage และอ่าน ลำดับใบ π=(π1,,πN)\pi = (\pi_1, \ldots, \pi_N) — การเรียงสับเปลี่ยนสินทรัพย์ที่สินทรัพย์ที่คล้ายกันอยู่ติดกัน

ขั้นตอนเสริม: จำนวน cluster ที่เหมาะสมสามารถเลือกได้ด้วย silhouette coefficient si=biaimax(bi,ai)s_i = \dfrac{b_i - a_i}{\max(b_i, a_i)} โดยที่ aia_i คือระยะทางเฉลี่ยภายใน cluster และ bib_i คือระยะทางเฉลี่ยไปยัง cluster เพื่อนบ้านที่ใกล้ที่สุด การผ่านพื้นฐานไม่ต้องการขั้นตอนนี้ — recursive bisection เคารพลำดับชั้นอยู่แล้ว

Quasi-diagonalization

เราเรียงแถวและคอลัมน์ของ Σ\Sigma ใหม่ตาม π\pi โดยรวมค่าขนาดใหญ่ไว้ตามแนวทแยง:

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

Recursive Bisection

จากนั้น recursion ทำงานจากบนลงล่าง ในแต่ละขั้น cluster จะถูกแบ่งครึ่งเป็น LL และ RR และทุนจะถูกจัดสรรระหว่างสองครึ่ง แบบผกผันตามความแปรปรวน (variance):

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

ความแปรปรวนของ cluster คำนวณบน covariance sub-block เป็น σC2=1m2i,jCΣi,jq\sigma_C^2 = \tfrac{1}{m^2}\sum_{i,j \in C}\Sigma^{q}_{i,j} การลงลึกดำเนินต่อจนแต่ละโหนดมีสินทรัพย์เดียว น้ำหนักเป็น long-only ไม่ติดลบ รวมกันได้ 1.0

ใน implementation ของเราสิ่งนี้คือฟังก์ชัน hrp_from_cov(cov) -> Vec<f64>: correlation → distance → average linkage → leaf order → quasi-diagonalization → recursive bisection Pipeline เรียกใช้มันเป็นฐาน — และยังเป็น optimize() สาธารณะสำหรับกรณีที่ไม่มีสัญญาณอีกด้วย

ขั้นตอน III. Long/Short Overlay

HRP ธรรมดาเป็นพอร์ตโฟลิโอ "ซื้ออย่างเดียว" แต่กลยุทธ์มักบอกไม่แค่ เท่าไร แต่ ทิศทางไหน ขั้นตอน III รับสัญญาณต่อสินทรัพย์ (Long/Short) จาก agent และสร้างสอง sub-portfolios

  1. สินทรัพย์ถูกแบ่งเป็นตะกร้า long และ short ตามสัญญาณ
  2. ภายใน แต่ละ ตะกร้า น้ำหนักจะคำนวณด้วย HRP เดิม (บน covariance sub-block ของสินทรัพย์เหล่านั้น) โดยรวมกันได้ 1 ต่อตะกร้า
  3. หาก agent ส่ง confidence pip_i ด้วย ส่วนแบ่งความเสี่ยงระหว่างสองฝั่งจะกำหนดจาก confidence รวม:

ξ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}

หากไม่มี confidence ส่วนแบ่งจะย้อนกลับไปใช้จำนวนสินทรัพย์ในแต่ละตะกร้า น้ำหนักสัญลักษณ์ขั้นสุดท้ายคือ wi=λLwiHRPw_i = \lambda_L \cdot w_i^{\text{HRP}} สำหรับ long และ wi=λSwiHRPw_i = -\lambda_S \cdot w_i^{\text{HRP}} สำหรับ short หลังจากนั้น gross exposure ทั้งหมดจะถูกนอร์มัลไลซ์ให้เท่ากับ 1

หมายเหตุตรงไปตรงมาเกี่ยวกับโค้ด ข้อกำหนดต้นฉบับมีตัวประกอบแก้ไข αL=λL/σL\alpha_L = \sqrt{\lambda_L}/\sigma_L, αS=λS/σS\alpha_S = \sqrt{\lambda_S}/\sigma_S — แต่ยังทำเครื่องหมายด้วยคำถามว่า "เราต้องการขั้นตอนนี้จริงหรือ?" การ implement ไม่ได้นำไปใช้: สองฝั่งถูกรวมกันโดยตรงด้วยส่วนแบ่งความเสี่ยง λ\lambda ซึ่งรักษา gross exposure ให้อยู่ที่ 1 พอดีและไม่สร้าง leverage ซ่อนเร้น นั่นเป็นการทำให้ข้อกำหนดเรียบง่ายโดยตั้งใจ ไม่ใช่ความผิดพลาด

ขั้นตอน IV. CVaR กับการปรับ Hull-White

HRP สมดุลความเสี่ยง เชิงโครงสร้าง แต่ไม่รู้อะไรเลยเกี่ยวกับระดับความเสี่ยงสัมบูรณ์ในแง่เงิน ขั้นตอนสุดท้ายวางเพดานแน่นบน tail risk — และทำให้มันไวต่อการเปลี่ยนแปลงของระบอบตลาด

Portfolio Return และความผันผวน EWMA

ก่อนอื่นเราสรุปน้ำหนักเป็น portfolio return และประเมินความผันผวนแบบมีเงื่อนไขด้วย 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

โดย λ=0.94\lambda = 0.94 (ค่าคลาสสิก RiskMetrics) EWMA ให้ความผันผวน "วันนี้" แทนที่จะเป็นค่าเฉลี่ยตลอดประวัติทั้งหมด

การปรับ Hull-White

แนวคิดหลัก: returns ในอดีตไม่สามารถนำมาใช้ตามที่เป็นได้ — มันเกิดขึ้นภายใต้ความผันผวน ที่แตกต่างกัน วิธี Hull-White ปรับขนาด return ในอดีตแต่ละตัวให้อยู่ในระดับปัจจุบัน:

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

เดือนที่เงียบสงบถูก "ยืด" เดือนที่ผันผวนถูก "บีบ" และการกระจายถูกนำมาอยู่ในระบอบปัจจุบัน

VaR และ CVaR

บนการกระจายที่ปรับขนาดแล้ว เรานำ loss quantile และค่าเฉลี่ย loss ใน tail:

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 (หรือที่เรียกว่า Expected Shortfall) ตอบคำถามไม่ใช่ "วันที่แย่ทั่วไปแย่แค่ไหน" แต่ "แย่แค่ไหน โดยเฉลี่ยทั่ว α\alpha เปอร์เซ็นต์ที่แย่ที่สุด" — ดังนั้นจึงเห็นความหนาของ tail ไม่แค่ขอบของมัน

งบประมาณความเสี่ยงและเงินสด

หาก CVaR เกินเกณฑ์ที่ยอมรับได้ ตำแหน่งที่มีความเสี่ยงทุกตัวจะถูกลดขนาดด้วยตัวประกอบเดียว และทุนที่ได้กลับมาจะถูกย้ายไปที่เงินสด:

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}|

ดังนั้นพอร์ตโฟลิโอจะลดความเสี่ยงเองเมื่อ tail risk เพิ่มขึ้น และกลับเข้าตลาดเมื่อมันสงบลง

จากข้อกำหนดสู่โค้ด

อัลกอริทึมทั้งหมดอยู่ใน Rust crate เดียว portfolio-pipeline และปฏิบัติตาม contract ของ workspace ที่เหมือนกัน:

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

นี่คือ long-only projection (ขั้นตอน I, II, IV ไม่มีสัญญาณ) — interface prices -> weights เดียวกันกับอัลกอริทึมอื่นอีกสิบเอ็ด ดังนั้น Pipeline จึงเป็น drop-in replacement สำหรับอัลกอริทึมใดก็ได้ เวอร์ชันเต็มที่มีทุกขั้นตอนเป็นฟังก์ชันแยก:

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 + σ

ค่าเริ่มต้น overlay: tail cvar_alpha = 0.05, budget cvar_max = 0.05, EWMA ewma_lambda = 0.94, Hull-White window hw_window = 0 (ประวัติทั้งหมด) การ implement ไม่มี external dependencies และมีการป้องกันอย่างตั้งใจ: สำหรับประวัติสั้น (น้อยกว่า 4 price points) จะคืน equal weights และ CVaR overlay จะเริ่มทำงานเมื่อมี ≥8 return observations เท่านั้น — มิฉะนั้นไม่มีอะไรให้ประเมิน tail

ทำไมต้อง Rust: codebase เดียวที่กำหนดได้สำหรับทั้ง backtest และ production โดยไม่มีการเลื่อนระหว่าง "Python ในการวิจัย สิ่งอื่นใน prod" และเร็วพอที่จะรันอัลกอริทึมทั้งสิบสองในคำขอเดียวผ่าน comparison backend

ราคาในแง่เวลา

"เร็วพอ" หมายความว่าอะไร? เราดึง HRP core (log returns → covariance → average linkage → quasi-diagonalization → recursive weights) ออกมาเป็น standalone benchmark และรันคณิตศาสตร์เดียวกันใน 7 ภาษา — C, C++, Rust, Zig, Python, Node.js และ Bun — ภายใต้เงื่อนไขเดียวกัน: Apple Silicon, single thread, 365 daily observations ต่อสินทรัพย์, synthetic prices, จำนวนสินทรัพย์ NN จาก 10 ถึง 10,000

คำพูดเกี่ยวกับความซับซ้อน เพราะมันกำหนดรูปร่างทั้งหมด เวอร์ชันตำราของ average linkage สแกน distance matrix เต็มเพื่อหาคู่ที่ใกล้ที่สุดในทุก merge — นั่นคือ O(N3)O(N^3) และกลายเป็น bottleneck ที่สินทรัพย์หลายพัน benchmark แทนที่ใช้อัลกอริทึม nearest-neighbour-chain แบบ O(N2)O(N^2) (Müllner 2011) — เช่นเดียวกับที่อยู่เบื้องหลัง SciPy's linkage(method='average') ด้วยสิ่งนี้ การ clustering ไม่ใช่ขั้นตอนที่ dominant อีกต่อไป: ที่ N=2000N = 2000 ใช้เวลา ~15 ms จาก ~0.5 s ต่อ pass ต้นทุนตอนนี้ถูก dominate โดย covariance matrix, O(N2T)O(N^2 \cdot T) — ขั้นตอนเดียวที่วิธีแบบ HRP หลีกเลี่ยงไม่ได้

สิ่งที่การรันแสดงให้เห็น (ตารางต่อภาษาเต็มรูปแบบและ script การทดสอบซ้ำด้วยคำสั่งเดียวอยู่ใน project repository):

  • สำหรับพอร์ตโฟลิโอที่สมจริง มันฟรี ตะกร้า crypto มีสินทรัพย์หลายสิบตัว ไม่ค่อยเกินร้อย ที่ N100N \le 100 การ pass HRP เต็มใช้ millisecond หลักเดียวแม้ใน Node และ microsecond ใน Rust/C การคำนวณน้ำหนักใหม่ทุก tick ไม่ใช่ปัญหา
  • Rust อยู่ในช่วง ~1.0–1.3× ของ C — อยู่ใน order of magnitude เดียวกัน ทั้งคู่ compile และผูกกันอย่างมีประสิทธิภาพเมื่อ NN ถึงพัน C เร็วกว่าเล็กน้อยในการคำนวณ arithmetic แต่ Rust ให้ predictability เดียวกันโดยไม่มี garbage collector และ UB
  • ขยายได้ถึงพันสินทรัพย์ ด้วย linkage แบบ O(N2)O(N^2) การ pass เต็มใช้ ~0.5 s ที่ N=2000N = 2000 และไม่กี่วินาทีที่ N=5000N = 5000 ในภาษา compile; แม้แต่ Node แบบ interpret ก็ผ่าน N=2000N = 2000 ภายในสองวินาที สิ่งที่ตั้งเพดานตอนนี้คือขั้นตอน covariance ไม่ใช่ clustering

ข้อสรุปเชิงปฏิบัติ: ที่ขนาดพอร์ตโฟลิโอของเรา การเลือก Rust ไม่ใช่เพื่อ "เอาชนะ C" (C เร็วกว่าเล็กน้อยที่นี่) — แต่เพื่อ codebase เดียวที่กำหนดได้สำหรับการวิจัยและ production โดยไม่มี GC pauses และมี performance headroom หลายปี benchmark 7 ภาษาเต็ม พร้อมผลลัพธ์และ script ทดสอบซ้ำ เปิดอยู่ใน project repository

Pipeline อยู่ตรงไหนในบรรดาสิบสอง

ในการเปรียบเทียบของเราบนตะกร้าเดียว (ที่จงใจทำให้เอนเอียง) Pipeline ทำงานเหมือน HRP — เพราะผ่าน entry point optimize() แบบ long-only มัน คือ HRP กับ CVaR overlay กลไกทิศทางของมันจะมีชีวิตชีวาก็ต่อเมื่อคุณป้อนสัญญาณกลยุทธ์ให้มัน นั่นคือจุดทั้งหมด: Pipeline ไม่ใช่ "optimizer อีกตัวสำหรับ backtest weights" แต่คือ execution layer ระหว่างสัญญาณกลยุทธ์และคำสั่งจริง — รับ buy/sell calls ของคุณ จัดทุนด้วย HRP ภายในแต่ละฝั่ง สมดุลฝั่งด้วย confidence และตัด tail risk ลงสู่งบประมาณที่กำหนด

สำหรับบริบทเต็ม — วิธีอื่นอีกสิบเอ็ดมีอะไรบ้างและต่างกันอย่างไร — ดูภาพรวม «12 อัลกอริทึมการปรับพอร์ตโฟลิโอ เปรียบเทียบกัน» และคุณสามารถทดลองทั้งหมดได้ที่ portfolio-optimizer.marketmaker.cc

อ้างอิง

  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

อ้างอิงบรรณานุกรม

@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/th/blog/post/portfolio-pipeline-hrp-cvar},
  description = {เจาะลึก Pipeline อัลกอริทึมการจัดสรรพอร์ตโฟลิโอแบบผสมที่สร้างบน Hierarchical Risk Parity ด้วย long/short overlay ที่ขับเคลื่อนด้วยสัญญาณและการปรับ Hull-White CVaR risk-budget พร้อมข้อกำหนดเต็มรูปแบบและ Rust implementation}
}
ข้อจำกัดความรับผิดชอบ: ข้อมูลที่ให้ไว้ในบทความนี้มีไว้เพื่อการศึกษาและให้ข้อมูลเท่านั้น และไม่ถือเป็นคำแนะนำทางการเงิน การลงทุน หรือการเทรด การเทรดสกุลเงินดิจิทัลมีความเสี่ยงสูงที่จะขาดทุน

ผู้เขียน

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

ก้าวนำหน้าตลาด

สมัครรับจดหมายข่าวของเราเพื่อรับข้อมูลเชิงลึกการเทรดด้วย AI เฉพาะ การวิเคราะห์ตลาด และการอัปเดตแพลตฟอร์ม

เราเคารพความเป็นส่วนตัวของคุณ ยกเลิกการสมัครได้ทุกเมื่อ