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

Conformal Prediction สำหรับการกำหนดขนาดสถานะแบบตระหนักความเสี่ยง

Conformal Prediction สำหรับการกำหนดขนาดสถานะแบบตระหนักความเสี่ยง
#uncertainty
#conformal-prediction
#risk
#position-sizing
#statistics
#algorithmic-trading
🧠
Part 4 of 4 · Collection
Deep Learning for Markets

ทุกสูตรการกำหนดขนาดสถานะ (position sizing) ต้องการการประมาณค่าความไม่แน่นอน เกณฑ์ Kelly ต้องการความน่าจะเป็นในการชนะและอัตราส่วนผลตอบแทน (ดู เกณฑ์ Kelly สำหรับกลยุทธ์) การหาค่าเหมาะสมแบบ mean-variance ต้องการเมทริกซ์ความแปรปรวนร่วม (covariance matrix) VaR ต้องการการแจกแจงของผลตอบแทน ทั้งหมดนี้ต้องการสมมติฐานเกี่ยวกับกระบวนการสร้างข้อมูล (data-generating process) ซึ่งเป็นสมมติฐานที่ตลาดการเงินมักจะละเมิดอยู่เสมอ

Conformal prediction นำเสนอสิ่งที่แตกต่างออกไป: ช่วงการพยากรณ์ที่มีการรับประกัน coverage แบบ finite-sample โดยไม่ต้องมีสมมติฐานเกี่ยวกับการแจกแจงแบบพารามิเตอร์ใด ๆ หากคุณขอ coverage 90% คุณก็จะได้ coverage อย่างน้อย 90% ไม่ว่าผลตอบแทนจะเป็นแบบ Gaussian, fat-tailed, เบ้ (skewed) หรือ heteroskedastic ก็ตาม ข้อกำหนดเพียงอย่างเดียวคือ exchangeability (หรือเงื่อนไขที่อ่อนกว่านั้น ดังที่เราจะได้เห็น)

โพสต์นี้ครอบคลุมทฤษฎี รูปแบบหลัก ๆ และการนำไปใช้งานจริงสำหรับการกำหนดขนาดสถานะใน Python

แนวคิดหลัก: Nonconformity Scores

การแจกแจงของ nonconformity (residual) scores พร้อมเครื่องหมายแสดงเกณฑ์ quantile — แก่นกลางของการปรับเทียบแบบ conformal

Conformal prediction ทำงานโดยการวัดว่าการสังเกตใหม่นั้น "แปลก" เพียงใดเมื่อเทียบกับข้อมูลในอดีต ความแปลกนี้ถูกวัดปริมาณด้วย nonconformity score ซึ่งเป็นฟังก์ชันใด ๆ ที่วัดว่าจุดข้อมูลหนึ่งสอดคล้องกับรูปแบบที่เห็นในข้อมูลส่วนที่เหลือได้แย่เพียงใด

สำหรับการถดถอย (regression — การพยากรณ์ค่าต่อเนื่องอย่างเช่นผลตอบแทน) nonconformity score ที่ง่ายที่สุดคือ residual แบบสัมบูรณ์:

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

โดยที่ μ^\hat{\mu} คือตัวพยากรณ์แบบจุด (point predictor) ใด ๆ ก็ได้ (linear regression, random forest, neural network — ไม่สำคัญ) และ (Xi,Yi)(X_i, Y_i) คือจุดข้อมูลหนึ่ง

ข้อมูลเชิงลึกที่สำคัญ: หากจุดข้อมูล (X1,Y1),,(Xn,Yn),(Xn+1,Yn+1)(X_1, Y_1), \ldots, (X_n, Y_n), (X_{n+1}, Y_{n+1}) มีคุณสมบัติ exchangeable แล้ว อันดับ (rank) ของ Rn+1R_{n+1} ในบรรดา R1,,Rn,Rn+1R_1, \ldots, R_n, R_{n+1} จะมีการแจกแจงแบบสม่ำเสมอ (uniform) บน {1,,n+1}\{1, \ldots, n+1\} นี่เป็นข้อเท็จจริงเชิงการจัดหมู่ (combinatorial) ล้วน ๆ — ไม่ต้องการสมมติฐานใด ๆ เกี่ยวกับการแจกแจงของ XX หรือ YY

จากความสม่ำเสมอของอันดับนี้ เราสามารถสร้างช่วงการพยากรณ์ที่มี coverage แบบ finite-sample ได้

Split Conformal Prediction

Split conformal prediction: ข้อมูลถูกแบ่งเป็น training และ calibration folds โดย calibration residuals สร้างช่วงการพยากรณ์

Split conformal prediction (Papadopoulos et al., 2002; Lei et al., 2018) เป็นรูปแบบที่ใช้งานได้จริงที่สุด อัลกอริทึมนั้นเรียบง่าย:

ขั้นที่ 1. แบ่งข้อมูลออกเป็น training set Dtrain\mathcal{D}_{\text{train}} และ calibration set Dcal={(X1,Y1),,(Xn,Yn)}\mathcal{D}_{\text{cal}} = \{(X_1, Y_1), \ldots, (X_n, Y_n)\}

ขั้นที่ 2. ฟิตโมเดลใด ๆ μ^\hat{\mu} บน Dtrain\mathcal{D}_{\text{train}}

ขั้นที่ 3. คำนวณ nonconformity scores บน calibration set:

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

ขั้นที่ 4. สำหรับระดับ miscoverage ที่ต้องการ α(0,1)\alpha \in (0, 1) ให้กำหนด q^\hat{q} เป็น empirical quantile ที่ (1α)(n+1)n\frac{\lceil (1 - \alpha)(n + 1) \rceil}{n} ของ R1,,RnR_1, \ldots, R_n กล่าวอย่างเป็นรูปธรรม นี่คือ residual ที่เล็กเป็นอันดับที่ (1α)(n+1)\lceil (1 - \alpha)(n + 1) \rceil (และ q^=+\hat{q} = +\infty เมื่อใดก็ตามที่ (1α)(n+1)>n\lceil (1 - \alpha)(n + 1) \rceil > n กล่าวคือสำหรับ nn ที่เล็กมาก)

ขั้นที่ 5. ช่วงการพยากรณ์สำหรับจุดใหม่ Xn+1X_{n+1} คือ:

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]

การรับประกัน Coverage

ภายใต้ exchangeability ของ calibration data และจุดทดสอบใหม่:

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

นี่คือการรับประกันแบบ finite-sample — ไม่ใช่การประมาณค่าเชิง asymptotic มันเป็นจริงสำหรับโมเดล μ^\hat{\mu} ใด ๆ การแจกแจงของข้อมูลแบบใดก็ได้ และขนาดตัวอย่าง nn เท่าใดก็ได้ หาก μ^\hat{\mu} เป็นตัวพยากรณ์ที่ห่วยแตก ช่วงก็จะกว้างขึ้นเท่านั้น การรับประกัน coverage ยังคงเป็นจริงอยู่

นอกจากนี้ยังมีขอบเขตบน (upper bound) เมื่อ scores ไม่มีค่าซ้ำกัน (no ties): 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} ดังนั้น coverage จึงไม่ได้อนุรักษนิยมจนสิ้นเปลือง

เหตุใดสิ่งนี้จึงสำคัญต่อการเทรด

ช่วงการพยากรณ์แบบดั้งเดิม เช่นจาก linear regression สมมติว่ามี error แบบ Gaussian ช่วงแบบ Gaussian ที่ปรับเทียบบนข้อมูลส่วนใหญ่อาจประเมินส่วนหาง (tails) ผิดอย่างร้ายแรงเมื่อ residual จริงเป็นแบบ heavy-tailed (เช่น Student-tt ที่มี degrees of freedom เพียงไม่กี่ค่า): มวลตรงกลางจะบางกว่าแบบ Gaussian ดังนั้นช่วง Gaussian ที่ตรงกับความแปรปรวน (variance-matched) จะ over-cover ใกล้จุดกึ่งกลางแต่ under-cover ในส่วนหาง ส่วนช่วงที่ฟิตกับส่วนหาง (tail-fit) ก็จะทำตรงข้าม ประเด็นไม่ได้อยู่ที่ตัวเลขมหัศจรรย์เพียงตัวเดียว — แต่อยู่ที่ว่า coverage ที่เกิดขึ้นจริงของช่วงแบบพารามิเตอร์นั้นขึ้นอยู่กับสมมติฐานเกี่ยวกับการแจกแจงที่คุณยังไม่ได้ตรวจสอบ

ช่วงการพยากรณ์แบบ conformal หลีกเลี่ยงปัญหานี้ได้ พวกมันขยายกว้างขึ้นโดยอัตโนมัติเมื่อโมเดลไม่แน่ใจ และรักษา marginal coverage ไว้ได้ไม่ว่าการแจกแจงของ error จริงจะเป็นอย่างไร สำหรับเทรดเดอร์ สิ่งนี้หมายความว่า:

  • หากคุณกำหนดขนาดสถานะแบบแปรผกผันกับความกว้างของช่วง คุณก็จะ ลดความเสี่ยง (exposure) โดยอัตโนมัติ เมื่อโมเดลไม่แน่ใจ
  • การรับประกัน coverage หมายความว่าการประมาณความเสี่ยงของคุณนั้น ซื่อสัตย์ — หากคุณบอกว่า "90% ของผลตอบแทนที่เกิดขึ้นจริงจะอยู่ภายในช่วงนี้" ข้อความนั้นจะถูกต้องในเชิงสถิติ (โดย marginal ภายใต้ exchangeability)

Full Conformal และ Jackknife+

Split conformal นั้นเรียบง่ายแต่สิ้นเปลืองข้อมูล: calibration set ไม่สามารถนำไปใช้ในการฝึก (training) ได้ มีสองทางเลือกที่จัดการเรื่องนี้

Full Conformal Prediction

Full conformal prediction (Vovk et al., 2005) ใช้ข้อมูลทั้งหมดทั้งในการฝึกและการปรับเทียบ สำหรับค่าผู้เข้าชิง (candidate value) yy แต่ละค่าของ Yn+1Y_{n+1}:

  1. เพิ่มชุดข้อมูลด้วย (Xn+1,y)(X_{n+1}, y)
  2. ฟิตโมเดลใหม่บนชุดข้อมูลที่เพิ่มแล้ว
  3. คำนวณ nonconformity scores ทั้งหมด
  4. รวม yy เข้าในเซตการพยากรณ์ หาก score สำหรับ (Xn+1,y)(X_{n+1}, y) ไม่สุดโต่งเกินไป

เซตการพยากรณ์คือ:

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

โดยที่ RiyR_i^y คือ nonconformity scores ที่คำนวณด้วยชุดข้อมูลที่เพิ่มแล้ว

Full conformal ให้ช่วงที่แคบที่สุด แต่มีต้นทุนการคำนวณที่สูงเกินไปสำหรับโมเดลส่วนใหญ่ — คุณต้องฟิตโมเดลใหม่สำหรับทุกค่าผู้เข้าชิง yy บนตาราง (grid) สำหรับการพยากรณ์ผลตอบแทน นี่อาจหมายถึงการฟิตใหม่หลายพันครั้งต่อการพยากรณ์หนึ่งครั้ง

Jackknife+ (Barber et al., 2021)

Jackknife+ สร้างความสมดุล มันใช้ residual แบบ leave-one-out (LOO) แต่คำนึงถึงความแปรปรวนในโมเดลที่ฟิตได้ข้าม LOO folds ต่าง ๆ

ให้ μ^i\hat{\mu}_{-i} แทนโมเดลที่ฝึกบนข้อมูลทั้งหมดยกเว้นจุด ii นิยาม LOO nonconformity score ด้วย residual แบบสัมบูรณ์ค่าเดียว:

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

จากนั้นช่วงการพยากรณ์แบบ jackknife+ จะถูกสร้างจากการพยากรณ์ LOO ที่จุดทดสอบ ขยายกว้างด้วย residual เหล่านี้:

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]

ในที่นี้ q1α+{vi}q_{1-\alpha}^{+}\{v_i\} แทนค่าที่เล็กเป็นอันดับที่ (1α)(n+1)\lceil (1-\alpha)(n+1)\rceil ของเซต {vi}i=1n\{v_i\}_{i=1}^n และ qα{vi}q_{\alpha}^{-}\{v_i\} แทนค่าที่เล็กเป็นอันดับที่ α(n+1)\lfloor \alpha(n+1)\rfloor ขอบเขตล่าง (lower bound) ลบ residual ออกจากการพยากรณ์ LOO แต่ละค่า ส่วนขอบเขตบน (upper bound) บวก มันเข้าไป ความไม่สมมาตรนั้นคือหัวใจสำคัญทั้งหมด — การยุบขอบเขตทั้งสองให้เหลือ μ^i+Ri\hat{\mu}_{-i} + R_i จะทำให้ขอบเขตล่างอยู่เหนือการพยากรณ์ ซึ่งเป็นสิ่งที่ผิด

Jackknife+ ให้การรับประกัน coverage อย่างน้อย 12α1 - 2\alpha (อ่อนกว่า 1α1-\alpha ของ split conformal เล็กน้อย) แต่มันใช้ข้อมูลทั้งหมดทั้งในการฝึกและการปรับเทียบ ในทางปฏิบัติ coverage มักจะใกล้เคียง 1α1-\alpha

สำหรับโมเดลการเทรดที่ฝึกบนข้อมูลจำกัด (เช่น โมเดลเฉพาะ regime ที่มีการสังเกตเพียงไม่กี่ร้อยตัว) jackknife+ มักเป็นตัวเลือกที่ดีที่สุด — มันไม่ต้องสละข้อมูลที่หายากไปเพื่อการปรับเทียบ ต้นทุนคือการฟิตโมเดลใหม่ nn ครั้ง

ปัญหากับอนุกรมเวลาทางการเงิน: Non-Exchangeability

Non-exchangeability ในอนุกรมเวลาทางการเงิน: อนุกรมที่ไม่นิ่ง (non-stationary) ที่มีการเปลี่ยน regime โดย coverage แตกร้าวเมื่อการแจกแจงเลื่อนไหล

การรับประกัน conformal มาตรฐานต้องการ exchangeability: การแจกแจงร่วม (joint distribution) ของ (Z1,,Zn+1)(Z_1, \ldots, Z_{n+1}) ไม่เปลี่ยนแปลงภายใต้การสับเปลี่ยน (permutations) สำหรับข้อมูลแบบ i.i.d. คุณสมบัตินี้เป็นจริงโดยปริยาย

อนุกรมเวลาทางการเงินไม่มีคุณสมบัติ exchangeable ผลตอบแทนแสดง:

  • Volatility clustering: ช่วงที่ความผันผวนสูงตามมาด้วยช่วงที่ความผันผวนสูง (GARCH effects)
  • Momentum และ mean reversion: สหสัมพันธ์ในตัวเอง (autocorrelation) ในผลตอบแทนหรือผลตอบแทนยกกำลังสอง
  • การเปลี่ยน Regime: การแจกแจงเลื่อนไปตามเวลา (ตลาดขาขึ้น vs. ตลาดขาลง)

หากคุณใช้ split conformal กับอนุกรมเวลาอย่างไร้เดียงสา — โดยใช้การแบ่ง calibration แบบสุ่ม — คุณจะละเมิดโครงสร้างเชิงเวลา calibration scores จากปี 2017 ที่สงบจะไม่สะท้อนความไม่แน่นอนของปี 2020 ที่ผันผวน การรับประกัน coverage ของคุณจะพังทลาย

Adaptive Conformal Inference (ACI)

Adaptive conformal inference: ช่วงการพยากรณ์ที่ขยายและหดผ่าน feedback loop ที่ติดตาม coverage ที่เกิดขึ้นจริงเข้าหาเป้าหมาย

Gibbs และ Candes (2021, NeurIPS) ได้นำเสนอ Adaptive Conformal Inference (ACI) เพื่อจัดการกับ distribution shift และข้อมูลที่ไม่มีคุณสมบัติ exchangeable แนวคิดนี้สง่างาม: แทนที่จะใช้ระดับ coverage ที่ตายตัว ให้ปรับ ระดับ miscoverage เป้าหมาย แบบออนไลน์โดยอิงว่าช่วงล่าสุดครอบคลุมผลลัพธ์จริงหรือไม่ และคำนวณ quantile ใหม่จากการแจกแจงของ score ในแต่ละขั้น

อัลกอริทึม ACI

ACI ไม่ได้ ขยับความกว้างของช่วงโดยตรง มันรักษาพารามิเตอร์ miscoverage แบบปรับตัว αt\alpha_t และคำนวณ conformal quantile ใหม่จากค่านั้น ในแต่ละขั้นเวลา tt:

  1. คำนวณเกณฑ์ conformal เป็น empirical (1αt)(1 - \alpha_t)-quantile ของเซต residual ปัจจุบัน (calibration scores บวกกับ realized scores ที่มีจนถึงตอนนี้): q^t=Quantile^1αt({Rj})\hat{q}_t = \widehat{\text{Quantile}}_{1-\alpha_t}(\{R_j\})
  2. สังเกต features XtX_t สร้างช่วง 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. สังเกตค่าจริง YtY_t และคำนวณตัวบ่งชี้ error errt=1{YtCt(Xt)}\text{err}_t = \mathbf{1}\{Y_t \notin C_t(X_t)\}
  4. อัปเดต ระดับ (ไม่ใช่ความกว้าง):

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

โดยที่ γ>0\gamma > 0 คือขนาดก้าว (step size) และ α\alpha คือ miscoverage เป้าหมาย หากช่วงพลาด (errt=1\text{err}_t = 1) αt\alpha_t จะหดลง ซึ่งผลัก quantile ถัดไปให้สูงขึ้นและขยายช่วงให้กว้างขึ้น หากครอบคลุมได้ αt\alpha_t จะโตขึ้นและช่วงจะแคบลง สิ่งสำคัญคือ γ\gamma ในที่นี้อยู่ใน หน่วยความน่าจะเป็น — มันขยับระดับใน [0,1][0,1] ไม่ใช่เกณฑ์ในหน่วยผลตอบแทนดิบ — ดังนั้น γ\gamma ค่าเดียวกันจึงทำงานได้อย่างสมเหตุสมผลไม่ว่า residual จะอยู่ในระดับ 10310^{-3} หรือไม่ก็ตาม

การรับประกัน Coverage สำหรับ ACI

ACI ให้การรับประกัน coverage ระยะยาวที่ไม่ขึ้นกับโมเดลการแจกแจง:

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}

เนื่องจาก αt\alpha_t ถูก clip ไว้ที่ [0,1][0,1] ตัวเศษจึงมีขอบเขตด้วย 1/γ1/\gamma คูณค่าคงที่ ดังนั้นด้านขวามือจึงเป็น O(1/T)O(1/T) และความถี่ของ empirical miscoverage ลู่เข้าสู่ α\alpha ข้อความที่ชัดเจน: ACI รับประกันว่าความถี่ของ empirical miscoverage ระยะยาวลู่เข้าสู่ α\alpha สำหรับลำดับใด ๆ ก็ตาม (รวมถึงลำดับที่เป็นปฏิปักษ์ — adversarial) ตราบใดที่ระดับที่ปรับแล้วยังอยู่ในขอบเขต — ซึ่ง clip บังคับให้เป็นเช่นนั้น มันเป็นการรับประกันเกี่ยวกับ ความถี่ ของ coverage ไม่ใช่ ความให้สารสนเทศ (informativeness) ของช่วง: ภายใต้ลำดับที่เป็นปฏิปักษ์อย่างแท้จริง ช่วงอาจขยายกว้างจนไร้สารสนเทศในขณะที่ยังคงบรรลุเป้าหมาย coverage

Dynamically-Tuned ACI (DtACI)

Gibbs และ Candes (2024, JMLR) ได้ปรับปรุง ACI ด้วย การปรับจูนแบบไดนามิก (dynamic tuning) ของขนาดก้าว γ\gamma แทนที่จะตรึง γ\gamma ไว้ พวกเขารักษาเซตผู้เข้าชิง Γ={γ1,,γK}\Gamma = \{\gamma_1, \ldots, \gamma_K\} และรวมพวกมันผ่านกฎการรวมผู้เชี่ยวชาญ (expert-aggregation rule) โดยให้น้ำหนัก γ\gamma ที่ coverage ล่าสุดใกล้เคียงกับเป้าหมายมากที่สุด

สิ่งนี้แก้ปัญหาเชิงปฏิบัติ: γ\gamma ที่ใหญ่จะปรับตัวอย่างรวดเร็วต่อการเปลี่ยน regime แต่สร้างความกว้างของช่วงที่ผันผวน ส่วน γ\gamma ที่เล็กจะเสถียรแต่ปรับตัวช้า DtACI แลกเปลี่ยนสิ่งเหล่านี้โดยอัตโนมัติ

เหตุใดสิ่งนี้จึงสำคัญต่อการเทรด

ลองพิจารณากลยุทธ์ market-making ที่ใช้โมเดลพยากรณ์ผลตอบแทน ในช่วงตลาดสงบ ช่วง conformal จะแคบ — โมเดลมั่นใจ และคุณสามารถถือสถานะที่ใหญ่ขึ้นได้ เมื่อความผันผวนพุ่งขึ้น (ฤดูประกาศผลกำไร, การประกาศของ FOMC, ความตื่นตระหนกทางภูมิรัฐศาสตร์) ระดับ ACI จะปรับตัวและช่วงจะขยายกว้างขึ้นภายในไม่กี่ขั้นเวลา การกำหนดขนาดสถานะของคุณจะหดลงเพื่อตอบสนอง โดยไม่ต้องมี โมเดลความผันผวนหรือตรรกะการตรวจจับ regime อย่างชัดแจ้งเลย

นี่คือการวัดปริมาณความไม่แน่นอนในฐานะสัญญาณชั้นหนึ่ง (first-class signal) ไม่ใช่สิ่งที่คิดทีหลัง

การกำหนดขนาดสถานะด้วยช่วง Conformal

การแมปช่วงความไม่แน่นอนที่ปรับเทียบแล้วไปสู่ขนาดสถานะ: ช่วงแคบขับเคลื่อนสถานะใหญ่ ส่วนช่วงกว้างขับเคลื่อนสถานะเล็ก

ตอนนี้เรามาเชื่อมโยง conformal prediction กับการกำหนดขนาดสถานะอย่างเป็นรูปธรรม ตัวแปรสำคัญคือ ครึ่งความกว้างของช่วงการพยากรณ์ (prediction interval half-width) เทียบกับกรณี residual สัมบูรณ์แบบสมมาตร เมื่อมีช่วงสมมาตร [μ^(Xt)q^t,  μ^(Xt)+q^t][\hat{\mu}(X_t) - \hat{q}_t, \; \hat{\mu}(X_t) + \hat{q}_t] ความกว้างเต็มคือ wt=2q^tw_t = 2\hat{q}_t เพื่อให้สูตรและโค้ดสอดคล้องกัน เราจะวัดทุกอย่างเทียบกับ ความกว้างเต็ม wtw_t ตลอดทั้งบทความ

การกำหนดขนาดแบบ Inverse-Width

วิธีที่ง่ายที่สุด: กำหนดขนาดแบบแปรผกผันกับความกว้างของช่วง

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

โดยที่ kk คือค่าคงที่ปรับสเกล (scaling constant) ที่ปรับเทียบให้เข้ากับงบความเสี่ยง (risk budget) ของคุณ เมื่อโมเดลมั่นใจ (ช่วงแคบ) คุณถือสถานะที่ใหญ่ขึ้น เมื่อไม่แน่ใจ (ช่วงกว้าง) คุณถือสถานะที่เล็กลง

นี่คล้ายกับ volatility targeting (size1/σ\text{size} \propto 1/\sigma) แต่มีความแตกต่างที่สำคัญ: ความกว้างของช่วง conformal เป็นมาตรวัดความไม่แน่นอนแบบ distribution-free ไม่ใช่การประมาณความผันผวนแบบพารามิเตอร์ มันจับความไม่แน่นอนเชิงพยากรณ์ภายใต้การรับประกัน coverage ไม่ใช่เพียงแค่ความแปรปรวนของผลตอบแทน

การกำหนดขนาดแบบ Edge-Ratio และตัวกรอง No-Trade

การกำหนดขนาดแบบ inverse-width ล้วน ๆ ละเลยความแรงของสัญญาณเอง การปรับแต่งที่เป็นธรรมชาติคือการปรับสเกลด้วย edge ratio — การพยากรณ์แบบจุดเทียบกับความกว้างของช่วง:

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

นี่คืออะนาล็อกแบบ conformal ของอัตราส่วนสัญญาณต่อสัญญาณรบกวน (signal-to-noise ratio): ผลตอบแทนคาดหวังหารด้วยมาตรวัดความไม่แน่นอนแบบ distribution-free เราใช้มันทั้งในการกำหนดขนาดและในตัวกรอง no-trade

ตัวกรองนี้มีหลักการ หากช่วงคร่อมศูนย์ (straddles zero)

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

แล้วช่วงการพยากรณ์ (1α)(1-\alpha) จะรวมทั้งผลตอบแทนบวกและลบ — ผลตอบแทนที่เกิดขึ้นจริงสามารถมีเครื่องหมายตรงข้ามกับการพยากรณ์ของคุณได้อย่างน่าเชื่อ นิยาม เกณฑ์ edge ขั้นต่ำ (minimum edge threshold) θ\theta และเทรดเฉพาะเมื่อ et>θe_t > \theta ข้อสังเกต: เนื้อหาเชิงเรขาคณิตของ "ete_t ใหญ่พอที่ช่วงจะไม่คร่อมศูนย์อีกต่อไป" คือ et>1/2e_t > 1/2 พอดี (เนื่องจากช่วงจะพ้นศูนย์เมื่อ μ^>q^t=wt/2|\hat{\mu}| > \hat{q}_t = w_t/2) เลือก θ\theta บนสเกลจริงของ et=μ^/wte_t = |\hat{\mu}|/w_t ผ่าน backtesting สำหรับ residual ของผลตอบแทนรายวัน ete_t มักจะต่ำกว่า 1/21/2 มาก ดังนั้น θ\theta ที่เล็กจิ๋วอาจยอมรับการเทรดเกือบทั้งหมด ส่วนค่าที่ใหญ่อาจไม่ยอมรับเลย ปรับเทียบมันให้เข้ากับข้อมูลของคุณ

เกี่ยวกับ "Conformal Kelly"

มันชวนให้อยากเอาช่วง conformal ไปติดเข้ากับ Kelly fraction f=pb(1p)bf^* = \frac{pb - (1-p)}{b} แต่ ff^* เป็นเศษส่วนที่สมบูรณ์และมีขอบเขตอยู่แล้ว ซึ่งได้มาจากความน่าจะเป็นในการชนะ pp และอัตราส่วนผลตอบแทน bb การคูณมันด้วยอัตราส่วนที่ไม่มีขอบเขตอย่าง μ^/q^\hat{\mu}/\hat{q} ไม่มีเหตุผลรองรับเชิงทฤษฎีการตัดสินใจ (decision-theoretic) เลย — มันสามารถเกิน 1 หรือพลิกเครื่องหมายได้อย่างเป็นอิสระจาก ff^* และมันนับ edge ที่ ff^* เข้ารหัสไว้แล้วซ้ำสองครั้ง ดังนั้นเราจึง ไม่ นำเสนอตัวคูณ "conformal Kelly"

หากคุณต้องการขับเคลื่อน Kelly จากช่วง คุณต้องหา pp และ bb จากมันจริง ๆ ซึ่งต้องการสมมติฐานที่ชัดเจนเกี่ยวกับการแจกแจง ภายใน ช่วง (ช่วง conformal จงใจไม่บอกอะไรเกี่ยวกับสิ่งนั้น — ดู Limitations) ตัวอย่างเช่น ภายใต้รูปร่างภายในช่วงที่สมมติไว้ คุณสามารถประมาณ pP(return>0)p \approx \mathbb{P}(\text{return} > 0) และอัตราส่วนผลตอบแทนจากเรขาคณิตของช่วงได้ — แต่ต้องประกาศสมมติฐานนั้นออกมาอย่างดังฟังชัด เพราะมันนำเอาความผูกพันแบบพารามิเตอร์ที่ conformal prediction ตั้งใจจะหลีกเลี่ยงกลับเข้ามาพอดี

ทางเลือกที่ซื่อสัตย์และพึ่งพาสมมติฐานน้อยคือการใช้ edge ratio et=μ^/wte_t = |\hat{\mu}|/w_t เป็น fractional-Kelly shrinkage: เพิ่มขนาดเมื่อผลตอบแทนคาดหวังใหญ่เมื่อเทียบกับช่วง ลดขนาดเมื่อมันเล็ก และใช้สิ่งนี้บนเพดานแข็ง (hard cap) — อย่างชัดเจนในฐานะ heuristic ไม่ใช่ในฐานะ "Kelly fraction"

การนำไปใช้งานใน Python

นี่คือการนำไปใช้งานจริง เราแสดงทั้งเส้นทาง split/prefit และเส้นทางเชิงเวลา (EnbPI) เนื่องจากประเด็นทั้งหมดของโพสต์นี้คือข้อมูลทางการเงินไม่มีคุณสมบัติ exchangeable

การตั้งค่าและการเตรียมข้อมูล

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 ด้วย MAPIE (Prefit)

สำหรับ split/prefit conformal นั้น cv="prefit" ต้องการ method="base" (ตัวประมาณ split-conformal แบบ naive) ตัวเลือก method="plus" คือตัวประมาณ CV+/jackknife+ และไม่เข้ากันกับ cv="prefit" — มันต้องการอ็อบเจกต์ cross-validation แทน เราใช้การจับคู่ที่ถูกต้องในที่นี้ และทำให้การกำหนดขนาดเป็นมาตรฐานบนความกว้างเต็ม

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)

Time-Series Conformal ด้วย EnbPI

เนื่องจากผลตอบแทนไม่มีคุณสมบัติ exchangeable การแบ่งแบบ random/prefit ข้างต้นจึงเป็นเพียงเส้นฐาน (baseline) เท่านั้น MapieTimeSeriesRegressor ของ MAPIE ที่ใช้ method="enbpi" (Xu & Xie, 2021) ใช้ block bootstrap และการอัปเดต residual ที่ออกแบบมาสำหรับความขึ้นต่อกันเชิงเวลา (temporal dependence) นี่คือเครื่องมือที่เข้ากันได้กับข้อโต้แย้งของโพสต์นี้เอง

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 (ออนไลน์)

สำหรับการเทรดสด เราจะนำ ACI จริงมาใช้งานจากศูนย์: รักษา ระดับ miscoverage αt\alpha_t อัปเดตมันแบบบวก (additively) และคำนวณ quantile ใหม่จากเซต residual ในแต่ละขั้น มีรายละเอียดแบบ finite-sample สองอย่างที่สำคัญ:

  • ใช้ order statistic ไม่ใช่ quantile แบบประมาณค่าระหว่าง (interpolated) np.quantile ทำ interpolation โดยปริยาย ซึ่งอาจตกลงต่ำกว่าอันดับที่กำหนดเล็กน้อยและทำให้ undercover ให้ส่ง method="higher" (เทียบเท่ากับ "inverted_cdf")
  • เมื่ออันดับที่กำหนดเกิน nn (nn เล็ก, coverage เป้าหมายสูง) เกณฑ์ที่ถูกต้องคือ ++\infty (ช่วง = ทั้งเส้นจำนวนจริง) ไม่ใช่การ clamp ไปที่ residual ที่ใหญ่ที่สุด การ clamp จะทำลายการรับประกัน 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))

นำมาประกอบกัน: ลูป 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

การประเมินผลลัพธ์

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

ข้อพิจารณาเชิงปฏิบัติ

การเลือก Nonconformity Score

Residual แบบสัมบูรณ์ Yμ^(X)|Y - \hat{\mu}(X)| เป็นค่าเริ่มต้น แต่มันสมมติว่าช่วงการพยากรณ์ควรสมมาตรรอบการพยากรณ์แบบจุด สำหรับผลตอบแทนทางการเงิน ช่วงแบบไม่สมมาตร (asymmetric intervals) มักสมเหตุสมผลกว่า:

  • Conformalized Quantile Regression (CQR): ฟิตตัวถดถอย quantile ที่ระดับ α/2\alpha/2 และ 1α/21 - \alpha/2 แล้วทำ conformalize (Romano et al., 2019) ช่วงจะปรับรูปร่างให้เข้ากับการแจกแจงเฉพาะที่ — กว้างขึ้นทางด้านขาลง (downside) ในช่วง drawdown และกว้างขึ้นทางด้านขาขึ้น (upside) ในช่วง rally (เมื่อใช้ CQR ช่วงจะไม่สมมาตรอีกต่อไป ดังนั้น wtw_t จึงเป็นความกว้างจริงแบบบนลบล่าง — ใช้ wtw_t เป็นตัวส่วนทุกที่ต่อไป)
  • Normalized scores: Ri=Yiμ^(Xi)/σ^(Xi)R_i = |Y_i - \hat{\mu}(X_i)| / \hat{\sigma}(X_i) โดยที่ σ^\hat{\sigma} คือการประมาณความผันผวนเฉพาะที่ (local volatility) สิ่งนี้สร้างช่วงที่ปรับตัวแบบมีเงื่อนไข (conditionally adaptive) — แคบลงในช่วง regime ความผันผวนต่ำ และกว้างขึ้นในช่วงความผันผวนสูง — ในขณะที่ยังคงรักษา marginal coverage

ขนาดของ Calibration Set

การรับประกัน coverage ของ split conformal เป็นจริงสำหรับขนาด calibration set nn ใด ๆ แต่ความกว้างของช่วงจะลดลงเมื่อ nn เพิ่มขึ้น สำหรับ nn ที่เล็กมาก order statistic ที่กำหนดอาจไม่มีอยู่ ซึ่งในกรณีนี้เกณฑ์ที่ซื่อสัตย์คือ ++\infty (ช่วงที่ไร้สารสนเทศแต่ถูกต้อง) — การ clamp ไปที่ residual ที่ใหญ่ที่สุดจะทำให้การรับประกันเป็นโมฆะอย่างเงียบ ๆ สำหรับการเทรดในทางปฏิบัติ:

  • n100n \geq 100 จุดปรับเทียบให้ช่วงที่แคบพอสมควร
  • n500n \geq 500 เป็นที่นิยมสำหรับการประมาณ quantile ที่เสถียร
  • เมื่อใช้ ACI calibration set มีไว้สำหรับการกำหนดค่าเริ่มต้นเท่านั้น การอัปเดตระดับแบบออนไลน์จะจัดการส่วนที่เหลือ

ความถี่ในการเทรนใหม่

โมเดลฐาน μ^\hat{\mu} อาจล้าสมัยได้ มีสองแนวทาง:

  1. เทรนใหม่เป็นระยะ (เช่น รายเดือน) และปรับเทียบ conformal quantile ใหม่
  2. ใช้ ACI และให้ระดับแบบปรับตัวชดเชยความล้าสมัยของโมเดล — ช่วงจะขยายกว้างขึ้นโดยอัตโนมัติเมื่อ residual ของโมเดลโตขึ้น

ทางเลือกที่ 2 นั้นง่ายกว่าและมีประสิทธิภาพอย่างน่าประหลาดใจ ชั้น conformal ทำหน้าที่เป็นตาข่ายนิรภัย (safety net): แม้ว่าโมเดลจะเสื่อมลง ความถี่ coverage ระยะยาวของ ACI ก็ยังคงลู่เข้าสู่เป้าหมาย

ต้นทุนการทำธุรกรรม

ช่วง conformal มีปฏิสัมพันธ์กับต้นทุนการทำธุรกรรม (transaction costs) ในแบบที่มีประโยชน์ เมื่อช่วงกว้าง (ความไม่แน่นอนสูง) สถานะจะเล็ก ดังนั้น turnover จึงต่ำ เมื่อช่วงแคบลง (โมเดลมั่นใจ) สถานะจะโต — แต่โมเดลมีแนวโน้มจะถูกมากกว่า ดังนั้น turnover จึงคุ้มค่าที่จะจ่าย

คุณยังสามารถรวมต้นทุนการทำธุรกรรมเข้าไปในตัวกรอง no-trade โดยตรงได้:

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

สิ่งนี้รับประกันว่าคุณเทรดเฉพาะเมื่อ edge สุทธิที่คาดหวังเกินเกณฑ์ที่ปรับสเกลด้วยความกว้าง conformal — โดยใช้ตัวส่วน wtw_t เดียวกันกับทุกที่

การเปรียบเทียบกับวิธีดั้งเดิม

คุณสมบัติ Gaussian CI Bootstrap CI Conformal PI
สมมติฐานการแจกแจง Normal errors i.i.d. + asymptotic ไม่มี (exchangeability)
การรับประกันแบบ finite-sample ไม่ ไม่ ใช่
ใช้ได้กับโมเดลใด ๆ ไม่ ใช่ ใช่
ปรับตัวต่อ heteroskedasticity ไม่ บางส่วน ด้วย CQR / normalized scores
จัดการ distribution shift ไม่ ไม่ รูปแบบ ACI / EnbPI
ต้นทุนการคำนวณ ต่ำ ปานกลาง Split: ต่ำ; jackknife+: O(n)O(n) refits; full: สูงเกินไป

Bootstrap เป็น "asymptotic" เฉพาะใน การรับประกัน ของมันเท่านั้น มันยังคง สมมติ ข้อมูลแบบ i.i.d./exchangeable และความเรียบ (smoothness) ดังนั้นช่องสมมติฐานการแจกแจงของมันจึงไม่ใช่ "ปลอดสมมติฐาน" และคอลัมน์ "conformal" เดียวซ่อนต้นทุนที่แตกต่างกันมากในแต่ละรูปแบบ ซึ่งแถวต้นทุนได้สะกดออกมาอย่างชัดเจนแล้ว

ข้อจำกัด

Conformal prediction ไม่ใช่เวทมนตร์ ข้อจำกัดสำคัญสำหรับการเทรด:

  1. Marginal ไม่ใช่ conditional coverage การรับประกันคือ P(Yn+1C(Xn+1))1α\mathbb{P}(Y_{n+1} \in C(X_{n+1})) \geq 1 - \alpha แบบ marginal — เฉลี่ยข้ามความสุ่มทั้งใน XX และ YY สำหรับค่าเงื่อนไขเฉพาะ X=xX = x coverage เฉพาะที่อาจสูงหรือต่ำกว่า 1α1 - \alpha Conformalized quantile regression แก้ปัญหานี้ได้บางส่วน

  2. Exchangeability เป็นข้อกำหนดที่แท้จริง สำหรับ split conformal calibration data และจุดทดสอบต้องมีคุณสมบัติ exchangeable ข้อมูลทางการเงินไม่มี ACI และ EnbPI ผ่อนปรนสิ่งนี้ให้เป็นการรับประกันระยะยาว แต่ coverage ในระยะสั้นอาจเบี่ยงเบนได้

  3. ความกว้างของช่วงไม่ใช่ความหนาแน่นของความน่าจะเป็น (probability density) ช่วง conformal บอกคุณว่า YY จะตกอยู่ที่ใดด้วยความน่าจะเป็น 1α1-\alpha แต่มันไม่ได้บอกอะไรเกี่ยวกับการแจกแจง ภายใน ช่วงเลย มันไม่ใช่สิ่งทดแทนการแจกแจงเชิงพยากรณ์เต็มรูปแบบ — ซึ่งนั่นคือเหตุผลที่การหา pp และ bb ของ Kelly จากมันต้องการสมมติฐานเพิ่มเติมที่ชัดเจน

  4. ขยะเข้า กว้างออก (Garbage in, wider out) โมเดลฐานที่แย่จะสร้างช่วงที่กว้าง Conformal prediction รับประกัน coverage แต่ไม่รับประกันความมีประโยชน์ หากโมเดลของคุณไม่มีพลังพยากรณ์ ช่วงจะกว้างเสียจนตัวกำหนดขนาดสถานะไม่เคยเทรดเลย

สรุป

Conformal prediction มอบกรอบงานที่เข้มงวดและ distribution-free สำหรับการวัดปริมาณความไม่แน่นอน ที่เข้ากันได้อย่างเป็นธรรมชาติกับการกำหนดขนาดสถานะ:

  • Split conformal สำหรับการปรับเทียบแบบ static, offline ที่มี coverage แบบ finite-sample
  • Jackknife+ เมื่อ calibration data หายากและคุณต้องการใช้การสังเกตทั้งหมด (โดยมีต้นทุนการฟิตใหม่ nn ครั้ง)
  • Adaptive conformal inference / EnbPI สำหรับการเทรดออนไลน์กับตลาดที่ไม่นิ่ง (non-stationary)
  • การกำหนดขนาดสถานะ ผ่านความกว้างช่วงแบบผกผัน (inverse interval width) และตัวกรอง no-trade แบบ edge-ratio — และหากคุณจะไปถึง Kelly ก็ต่อเมื่อหา pp และ bb มาอย่างซื่อสัตย์เท่านั้น ไม่ใช่ผ่านตัวคูณที่ไม่มีเหตุผลรองรับ

ข้อได้เปรียบสำคัญเหนือทางเลือกแบบพารามิเตอร์: คุณไม่จำเป็นต้องระบุหรือตรวจสอบสมมติฐานการแจกแจงแบบพารามิเตอร์เลย ช่วงต่าง ๆ ซื่อสัตย์โดยการสร้าง (by construction) (แบบ marginal ภายใต้ exchangeability) สำหรับเทรดเดอร์เชิงระบบ สิ่งนี้หมายถึงแหล่งความเสี่ยงของโมเดล (model risk) ที่ลดลงไปหนึ่งแหล่ง — และในธุรกิจที่ความเสี่ยงของโมเดลเป็นเรื่องคอขาดบาดตาย นั่นเป็นเรื่องสำคัญ


เอกสารอ้างอิง:

  • 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.
ข้อจำกัดความรับผิดชอบ: ข้อมูลที่ให้ไว้ในบทความนี้มีไว้เพื่อการศึกษาและให้ข้อมูลเท่านั้น และไม่ถือเป็นคำแนะนำทางการเงิน การลงทุน หรือการเทรด การเทรดสกุลเงินดิจิทัลมีความเสี่ยงสูงที่จะขาดทุน

ผู้เขียน

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

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

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

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