← Quay lại danh sách bài viết
June 12, 2026
5 phút đọc

Conformal Prediction cho việc định cỡ vị thế nhận biết rủi ro

Conformal Prediction cho việc định cỡ vị thế nhận biết rủi ro
#uncertainty
#conformal-prediction
#rủi-ro
#position-sizing
#thống-kê
#algorithmic-trading
🧠
Part 4 of 4 · Collection
Deep Learning for Markets

Mọi công thức định cỡ vị thế đều cần một ước lượng về độ bất định. Tiêu chí Kelly cần một xác suất thắng và một tỷ lệ chi trả (xem Tiêu chí Kelly cho các chiến lược). Tối ưu hóa mean-variance cần một ma trận hiệp phương sai. VaR cần một phân phối lợi suất. Tất cả những thứ này đều đòi hỏi các giả định về quá trình sinh dữ liệu — những giả định mà thị trường tài chính thường xuyên vi phạm.

Conformal prediction mang lại một thứ khác: khoảng dự báo với bảo đảm mức bao phủ trên mẫu hữu hạn, mà không cần bất kỳ giả định phân phối tham số nào. Nếu bạn yêu cầu mức bao phủ 90%, bạn nhận được ít nhất 90% mức bao phủ — bất kể lợi suất là Gauss, đuôi nặng, lệch, hay phương sai thay đổi. Yêu cầu duy nhất là tính hoán đổi được (exchangeability) (hoặc các điều kiện yếu hơn, như chúng ta sẽ thấy).

Bài viết này trình bày lý thuyết, các biến thể quan trọng, và một cài đặt thực tế để định cỡ vị thế bằng Python.

Ý tưởng cốt lõi: điểm bất phù hợp (nonconformity score)

Phân phối của các điểm bất phù hợp (phần dư) với một ngưỡng phân vị được đánh dấu — cốt lõi của hiệu chỉnh conformal

Conformal prediction hoạt động bằng cách đo lường một quan sát mới "lạ lùng" đến mức nào so với dữ liệu quá khứ. Sự lạ lùng được định lượng bằng một điểm bất phù hợp (nonconformity score) — bất kỳ hàm nào đo lường mức độ một điểm dữ liệu không phù hợp với khuôn mẫu thấy được ở phần còn lại của dữ liệu.

Với bài toán hồi quy (dự báo một giá trị liên tục như lợi suất), điểm bất phù hợp đơn giản nhất là phần dư tuyệt đối:

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

trong đó μ^\hat{\mu} là một bộ dự báo điểm bất kỳ (hồi quy tuyến tính, random forest, mạng nơ-ron — đều không quan trọng) và (Xi,Yi)(X_i, Y_i) là một điểm dữ liệu.

Nhận xét then chốt: nếu các điểm dữ liệu (X1,Y1),,(Xn,Yn),(Xn+1,Yn+1)(X_1, Y_1), \ldots, (X_n, Y_n), (X_{n+1}, Y_{n+1}) là hoán đổi được, thì hạng (rank) của Rn+1R_{n+1} trong số R1,,Rn,Rn+1R_1, \ldots, R_n, R_{n+1} phân phối đều trên {1,,n+1}\{1, \ldots, n+1\}. Đây là một sự kiện thuần túy tổ hợp — nó không đòi hỏi bất kỳ giả định nào về phân phối của XX hay YY.

Từ tính đều của hạng này, chúng ta có thể xây dựng các khoảng dự báo với mức bao phủ trên mẫu hữu hạn.

Split Conformal Prediction

Split conformal prediction: dữ liệu được chia thành các fold huấn luyện và hiệu chỉnh, các phần dư hiệu chỉnh tạo ra khoảng dự báo

Split conformal prediction (Papadopoulos et al., 2002; Lei et al., 2018) là biến thể thực tế nhất. Thuật toán đơn giản:

Bước 1. Chia dữ liệu thành một tập huấn luyện Dtrain\mathcal{D}_{\text{train}} và một tập hiệu chỉnh Dcal={(X1,Y1),,(Xn,Yn)}\mathcal{D}_{\text{cal}} = \{(X_1, Y_1), \ldots, (X_n, Y_n)\}.

Bước 2. Khớp một mô hình bất kỳ μ^\hat{\mu} trên Dtrain\mathcal{D}_{\text{train}}.

Bước 3. Tính các điểm bất phù hợp trên tập hiệu chỉnh:

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

Bước 4. Với một mức không bao phủ mong muốn α(0,1)\alpha \in (0, 1), lấy q^\hat{q} là phân vị thực nghiệm thứ (1α)(n+1)n\frac{\lceil (1 - \alpha)(n + 1) \rceil}{n} của R1,,RnR_1, \ldots, R_n. Cụ thể, đây là phần dư nhỏ thứ (1α)(n+1)\lceil (1 - \alpha)(n + 1) \rceil (và q^=+\hat{q} = +\infty mỗi khi (1α)(n+1)>n\lceil (1 - \alpha)(n + 1) \rceil > n, tức là với nn rất nhỏ).

Bước 5. Khoảng dự báo cho một điểm mới Xn+1X_{n+1} là:

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]

Bảo đảm mức bao phủ

Dưới giả định hoán đổi được của dữ liệu hiệu chỉnh và điểm kiểm tra mới:

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

Đây là một bảo đảm trên mẫu hữu hạn — không phải một xấp xỉ tiệm cận. Nó đúng với mô hình μ^\hat{\mu} bất kỳ, phân phối dữ liệu bất kỳ, và cỡ mẫu nn bất kỳ. Nếu μ^\hat{\mu} là một bộ dự báo tồi tệ, các khoảng sẽ đơn giản là rộng hơn. Bảo đảm mức bao phủ vẫn đúng.

Cũng có một cận trên khi các điểm số không có giá trị trùng nhau (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}, nên mức bao phủ không bảo thủ một cách lãng phí.

Vì sao điều này quan trọng với giao dịch

Các khoảng dự báo truyền thống từ, chẳng hạn, một hồi quy tuyến tính giả định sai số Gauss. Một khoảng Gauss được hiệu chỉnh trên phần lớn dữ liệu có thể đánh giá sai nghiêm trọng các đuôi khi phần dư thực có đuôi nặng (ví dụ Student-tt với vài bậc tự do): khối trung tâm mỏng hơn so với Gauss, nên một khoảng Gauss khớp phương sai sẽ bao phủ thừa gần trung tâm nhưng bao phủ thiếu ở đuôi, còn một khoảng khớp theo đuôi thì làm ngược lại. Điểm mấu chốt không phải là một con số kỳ diệu duy nhất — mà là mức bao phủ thực hiện được của một khoảng tham số phụ thuộc vào một giả định phân phối mà bạn chưa kiểm chứng.

Các khoảng conformal prediction né được điều này. Chúng tự động nới rộng khi mô hình không chắc chắn, và chúng duy trì mức bao phủ biên (marginal coverage) bất kể phân phối sai số thực là gì. Với một nhà giao dịch, điều này có nghĩa là:

  • Nếu bạn định cỡ vị thế tỷ lệ nghịch với độ rộng khoảng, bạn tự động giảm phơi nhiễm khi mô hình không chắc chắn.
  • Bảo đảm mức bao phủ có nghĩa là các ước lượng rủi ro của bạn trung thực — nếu bạn nói "90% lợi suất thực hiện sẽ rơi vào trong khoảng này," thì phát biểu đó hợp lệ về mặt thống kê (theo nghĩa biên, dưới giả định hoán đổi được).

Full Conformal và Jackknife+

Split conformal đơn giản nhưng lãng phí dữ liệu: tập hiệu chỉnh không thể được dùng để huấn luyện. Hai lựa chọn thay thế giải quyết điều này.

Full Conformal Prediction

Full conformal prediction (Vovk et al., 2005) dùng toàn bộ dữ liệu cho cả huấn luyện lẫn hiệu chỉnh. Với mỗi giá trị ứng viên yy của Yn+1Y_{n+1}:

  1. Bổ sung tập dữ liệu với (Xn+1,y)(X_{n+1}, y).
  2. Khớp lại mô hình trên tập dữ liệu đã bổ sung.
  3. Tính tất cả các điểm bất phù hợp.
  4. Đưa yy vào tập dự báo nếu điểm số cho (Xn+1,y)(X_{n+1}, y) không quá cực đoan.

Tập dự báo là:

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

trong đó RiyR_i^y là các điểm bất phù hợp được tính với tập dữ liệu đã bổ sung.

Full conformal cho ra các khoảng chặt nhất nhưng tốn kém tính toán đến mức không khả thi với hầu hết các mô hình — bạn phải khớp lại mô hình cho từng giá trị ứng viên yy trên một lưới. Với một dự báo lợi suất, điều này có thể có nghĩa là hàng nghìn lần khớp lại cho mỗi lần dự báo.

Jackknife+ (Barber et al., 2021)

Jackknife+ tạo ra một sự cân bằng. Nó dùng các phần dư bỏ-một-ra (leave-one-out, LOO) nhưng tính đến tính biến thiên trong mô hình khớp được qua các fold LOO.

Gọi μ^i\hat{\mu}_{-i} là mô hình được huấn luyện trên toàn bộ dữ liệu trừ điểm ii. Định nghĩa điểm bất phù hợp LOO với phần dư tuyệt đối đơn:

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

Khoảng dự báo jackknife+ khi đó được xây dựng từ các dự báo LOO tại điểm kiểm tra, được nới rộng bởi các phần dư này:

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]

Ở đây q1α+{vi}q_{1-\alpha}^{+}\{v_i\} ký hiệu giá trị nhỏ thứ (1α)(n+1)\lceil (1-\alpha)(n+1)\rceil của tập {vi}i=1n\{v_i\}_{i=1}^n, và qα{vi}q_{\alpha}^{-}\{v_i\} là giá trị nhỏ thứ α(n+1)\lfloor \alpha(n+1)\rfloor. Cận dưới trừ phần dư khỏi mỗi dự báo LOO; cận trên cộng nó vào. Sự bất đối xứng đó chính là toàn bộ điểm mấu chốt — gộp cả hai cận thành μ^i+Ri\hat{\mu}_{-i} + R_i sẽ đẩy cận dưới lên trên dự báo, điều đó là sai.

Jackknife+ cung cấp một bảo đảm mức bao phủ ít nhất 12α1 - 2\alpha (yếu hơn một chút so với 1α1-\alpha của split conformal), nhưng nó dùng toàn bộ dữ liệu cho cả huấn luyện lẫn hiệu chỉnh. Trong thực tế mức bao phủ thường gần với 1α1-\alpha.

Với các mô hình giao dịch được huấn luyện trên dữ liệu hạn chế (ví dụ, các mô hình theo từng chế độ thị trường (regime) chỉ với vài trăm quan sát), jackknife+ thường là lựa chọn tốt nhất — nó không hy sinh dữ liệu khan hiếm cho việc hiệu chỉnh. Cái giá phải trả là nn lần khớp lại mô hình.

Vấn đề với chuỗi thời gian tài chính: tính không hoán đổi được

Tính không hoán đổi được trong chuỗi thời gian tài chính: một chuỗi không dừng với một sự dịch chuyển chế độ, mức bao phủ rạn nứt khi phân phối trôi dạt

Bảo đảm conformal chuẩn đòi hỏi tính hoán đổi được: phân phối đồng thời của (Z1,,Zn+1)(Z_1, \ldots, Z_{n+1}) bất biến dưới các phép hoán vị. Với dữ liệu i.i.d., điều này đúng một cách hiển nhiên.

Chuỗi thời gian tài chính không hoán đổi được. Lợi suất thể hiện:

  • Phân cụm biến động (volatility clustering): Các giai đoạn biến động cao theo sau các giai đoạn biến động cao (hiệu ứng GARCH).
  • Động lượng và hồi quy về trung bình: Tự tương quan trong lợi suất hoặc lợi suất bình phương.
  • Thay đổi chế độ (regime changes): Phân phối dịch chuyển theo thời gian (thị trường tăng giá so với giảm giá).

Nếu bạn áp dụng split conformal một cách ngây thơ lên một chuỗi thời gian — dùng một phép chia hiệu chỉnh ngẫu nhiên — bạn vi phạm cấu trúc thời gian. Các điểm số hiệu chỉnh từ một năm 2017 yên ả sẽ không phản ánh độ bất định của một năm 2020 đầy biến động. Bảo đảm mức bao phủ của bạn sụp đổ.

Adaptive Conformal Inference (ACI)

Adaptive conformal inference: một khoảng dự báo nới rộng và thu hẹp qua một vòng phản hồi theo dõi mức bao phủ thực hiện được hướng về mục tiêu

Gibbs và Candes (2021, NeurIPS) giới thiệu Adaptive Conformal Inference (ACI) để xử lý dịch chuyển phân phối và dữ liệu không hoán đổi được. Ý tưởng tinh tế: thay vì dùng một mức bao phủ cố định, hãy điều chỉnh mức không bao phủ mục tiêu một cách trực tuyến dựa trên việc các khoảng gần đây có bao phủ kết cục thực hay không, và suy ra lại phân vị từ phân phối điểm số ở mỗi bước.

Thuật toán ACI

ACI không nhúc nhích độ rộng khoảng một cách trực tiếp. Nó duy trì một tham số không bao phủ thích nghi αt\alpha_t và tính lại phân vị conformal từ đó. Tại mỗi bước thời gian tt:

  1. Tính ngưỡng conformal là phân vị thực nghiệm (1αt)(1 - \alpha_t) của tập phần dư hiện tại (các điểm số hiệu chỉnh, cộng với bất kỳ điểm số thực hiện được nào cho đến nay): q^t=Quantile^1αt({Rj})\hat{q}_t = \widehat{\text{Quantile}}_{1-\alpha_t}(\{R_j\}).
  2. Quan sát các đặc trưng XtX_t, tạo ra khoảng 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. Quan sát giá trị thực YtY_t và tính chỉ báo lỗi errt=1{YtCt(Xt)}\text{err}_t = \mathbf{1}\{Y_t \notin C_t(X_t)\}.
  4. Cập nhật mức (không phải độ rộng):

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

trong đó γ>0\gamma > 0 là một bước nhảy và α\alpha là mức không bao phủ mục tiêu. Nếu một khoảng trượt (errt=1\text{err}_t = 1), αt\alpha_t co lại, điều này đẩy phân vị kế tiếp cao hơn và nới rộng khoảng; nếu nó bao phủ, αt\alpha_t tăng và các khoảng thắt chặt lại. Quan trọng là, γ\gamma ở đây tính theo đơn vị xác suất — nó nhúc nhích một mức trong [0,1][0,1], không phải ngưỡng theo đơn vị lợi suất thô — nên cùng một γ\gamma ứng xử hợp lý dù phần dư ở cỡ 10310^{-3} hay không.

Bảo đảm mức bao phủ cho ACI

ACI cung cấp một bảo đảm mức bao phủ trong dài hạn không phụ thuộc vào một mô hình phân phối:

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 bị kẹp trong [0,1][0,1], tử số bị chặn bởi 1/γ1/\gamma nhân một hằng số, nên vế phải là O(1/T)O(1/T) và tần suất không bao phủ thực nghiệm hội tụ về α\alpha. Phát biểu chính xác: ACI bảo đảm tần suất không bao phủ thực nghiệm dài hạn hội tụ về α\alpha với các chuỗi tùy ý (kể cả chuỗi đối kháng), miễn là các mức được điều chỉnh giữ trong vùng bị chặn — điều mà phép kẹp ép buộc. Đây là một bảo đảm về tần suất bao phủ, không phải về tính thông tin của khoảng: dưới một chuỗi thực sự đối kháng, các khoảng có thể nới rộng đến mức vô bổ trong khi vẫn đạt được mục tiêu bao phủ.

ACI tinh chỉnh động (DtACI)

Gibbs và Candes (2024, JMLR) cải tiến ACI bằng tinh chỉnh động bước nhảy γ\gamma. Thay vì cố định γ\gamma, họ duy trì một tập ứng viên Γ={γ1,,γK}\Gamma = \{\gamma_1, \ldots, \gamma_K\} và kết hợp chúng qua một quy tắc tổng hợp chuyên gia (expert-aggregation), ưu tiên γ\gamma có mức bao phủ gần đây gần với mục tiêu nhất.

Điều này giải quyết một vấn đề thực tiễn: một γ\gamma lớn thích nghi nhanh với các thay đổi chế độ nhưng tạo ra độ rộng khoảng dao động mạnh; một γ\gamma nhỏ thì ổn định nhưng chậm thích nghi. DtACI đánh đổi giữa hai thứ này một cách tự động.

Vì sao điều này quan trọng với giao dịch

Hãy xét một chiến lược tạo lập thị trường (market-making) dùng một mô hình dự báo lợi suất. Trong những thị trường yên ả, các khoảng conformal chặt — mô hình tự tin, và bạn có thể nắm các vị thế lớn hơn. Khi biến động vọt lên (mùa báo cáo lợi nhuận, công bố của FOMC, các cú sốc địa chính trị), mức ACI thích nghi và các khoảng nới rộng trong vòng vài bước thời gian. Việc định cỡ vị thế của bạn co lại để phản ứng, mà không cần bất kỳ mô hình biến động tường minh hay logic phát hiện chế độ nào.

Đây là việc định lượng độ bất định như một tín hiệu hạng nhất, không phải một thứ nghĩ thêm sau cùng.

Định cỡ vị thế với các khoảng conformal

Ánh xạ một khoảng độ bất định đã hiệu chỉnh sang cỡ vị thế: một khoảng chặt dẫn đến một vị thế lớn, một khoảng rộng dẫn đến một vị thế nhỏ

Bây giờ hãy kết nối conformal prediction với việc định cỡ vị thế cụ thể. Biến then chốt là nửa độ rộng khoảng dự báo so với trường hợp phần dư tuyệt đối đối xứng. Với khoảng đối xứng [μ^(Xt)q^t,  μ^(Xt)+q^t][\hat{\mu}(X_t) - \hat{q}_t, \; \hat{\mu}(X_t) + \hat{q}_t], độ rộng đầy đủ là wt=2q^tw_t = 2\hat{q}_t. Để giữ cho các công thức và mã nhất quán, chúng ta đo mọi thứ theo độ rộng đầy đủ wtw_t xuyên suốt.

Định cỡ nghịch đảo độ rộng

Cách tiếp cận đơn giản nhất: định cỡ tỷ lệ nghịch với độ rộng khoảng.

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

trong đó kk là một hằng số tỷ lệ được hiệu chỉnh theo ngân sách rủi ro của bạn. Khi mô hình tự tin (khoảng hẹp), bạn nắm một vị thế lớn hơn. Khi không chắc chắn (khoảng rộng), bạn nắm một vị thế nhỏ hơn.

Điều này tương tự với nhắm mục tiêu biến động (volatility targeting) (size1/σ\text{size} \propto 1/\sigma), nhưng với một khác biệt then chốt: độ rộng khoảng conformal là một thước đo độ bất định không phụ thuộc phân phối, không phải một ước lượng biến động tham số. Nó nắm bắt độ bất định dự báo dưới bảo đảm mức bao phủ, không chỉ phương sai lợi suất.

Định cỡ theo tỷ số biên lợi (edge-ratio) và bộ lọc không-giao-dịch

Định cỡ nghịch đảo độ rộng thuần túy bỏ qua chính sức mạnh của tín hiệu. Một cải tiến tự nhiên là tỷ lệ theo tỷ số biên lợi (edge ratio) — dự báo điểm so với độ rộng khoảng:

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

Đây là một phép tương tự conformal của tỷ số tín hiệu trên nhiễu: lợi suất kỳ vọng chia cho một thước đo độ bất định không phụ thuộc phân phối. Chúng ta dùng nó cho cả việc định cỡ lẫn một bộ lọc không-giao-dịch.

Bộ lọc này có nguyên tắc. Nếu khoảng nằm vắt qua số không,

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

thì khoảng dự báo (1α)(1-\alpha) bao gồm cả lợi suất dương lẫn âm — lợi suất thực hiện được có thể hợp lý có dấu ngược với dự báo của bạn. Định nghĩa một ngưỡng biên lợi tối thiểu θ\theta và chỉ giao dịch khi et>θe_t > \theta. Lưu ý rằng nội dung hình học của "ete_t đủ lớn để khoảng không còn vắt qua số không" chính xác là et>1/2e_t > 1/2 (vì khoảng vượt khỏi số không khi μ^>q^t=wt/2|\hat{\mu}| > \hat{q}_t = w_t/2). Hãy chọn θ\theta trên thang đo thực tế của et=μ^/wte_t = |\hat{\mu}|/w_t thông qua backtesting; với phần dư lợi suất theo ngày, ete_t thường thấp hơn nhiều so với 1/21/2, nên một θ\theta rất nhỏ có thể chấp nhận gần như mọi giao dịch còn một θ\theta lớn có thể không chấp nhận giao dịch nào. Hãy hiệu chỉnh nó theo dữ liệu của bạn.

Về "Conformal Kelly"

Thật cám dỗ khi gắn các khoảng conformal vào phần Kelly f=pb(1p)bf^* = \frac{pb - (1-p)}{b}. Nhưng ff^* đã là một phần đầy đủ, bị chặn, được suy ra từ một xác suất thắng pp và một tỷ lệ chi trả bb; nhân nó với một tỷ số không bị chặn như μ^/q^\hat{\mu}/\hat{q} không có biện minh nào về mặt lý thuyết quyết định — nó có thể vượt quá 1 hoặc đổi dấu một cách độc lập với ff^*, và nó đếm trùng biên lợi mà ff^* đã mã hóa. Vì vậy chúng ta không trình bày một bộ nhân "conformal Kelly".

Nếu bạn muốn dẫn dắt Kelly từ khoảng, bạn phải thực sự suy ra ppbb từ nó, điều này đòi hỏi một giả định tường minh về phân phối bên trong khoảng (các khoảng conformal cố ý không nói gì về điều đó — xem mục Hạn chế). Ví dụ, dưới một dạng giả định bên-trong-khoảng, bạn có thể xấp xỉ pP(lợi suaˆˊt>0)p \approx \mathbb{P}(\text{lợi suất} > 0) và một tỷ lệ chi trả từ hình học của khoảng — nhưng hãy nêu rõ giả định đó thật to, bởi vì nó tái đưa vào đúng cái cam kết tham số mà conformal prediction vốn nhằm tránh.

Lựa chọn thay thế trung thực, nhẹ-giả-định là dùng tỷ số biên lợi et=μ^/wte_t = |\hat{\mu}|/w_t như một co rút Kelly phân số (fractional-Kelly shrinkage): tăng cỡ khi lợi suất kỳ vọng lớn so với khoảng, giảm cỡ khi nó nhỏ, và áp dụng điều này lên trên một mức trần cứng — một cách tường minh như một heuristic, không phải như "phần Kelly".

Cài đặt bằng Python

Đây là một cài đặt thực tế. Chúng tôi trình bày cả lối split/prefit lẫn lối thời gian (EnbPI), bởi vì toàn bộ điểm mấu chốt của bài viết này là dữ liệu tài chính không hoán đổi được.

Thiết lập và chuẩn bị dữ liệu

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 với MAPIE (Prefit)

Với conformal split/prefit, cv="prefit" đòi hỏi method="base" (bộ ước lượng split-conformal ngây thơ). Tùy chọn method="plus" là bộ ước lượng CV+/jackknife+ và không tương thích với cv="prefit" — nó cần một đối tượng cross-validation thay thế. Chúng tôi dùng tổ hợp đúng ở đây, và chuẩn hóa việc định cỡ theo độ rộng đầy đủ.

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 chuỗi thời gian với EnbPI

Bởi vì lợi suất không hoán đổi được, phép chia ngẫu nhiên/prefit ở trên chỉ là một mốc cơ sở. MapieTimeSeriesRegressor của MAPIE với method="enbpi" (Xu & Xie, 2021) dùng block bootstrap và cập nhật phần dư được thiết kế cho sự phụ thuộc thời gian. Đây là công cụ khớp với chính lập luận của bài viết.

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 (Trực tuyến)

Cho giao dịch trực tiếp, chúng tôi cài đặt ACI thực sự từ đầu: duy trì mức không bao phủ αt\alpha_t, cập nhật nó theo phép cộng, và suy ra lại phân vị từ tập phần dư ở mỗi bước. Hai chi tiết về mẫu hữu hạn quan trọng:

  • Dùng thống kê thứ tự (order statistic), không phải một phân vị nội suy. np.quantile nội suy theo mặc định, điều này có thể rơi xuống ngay dưới hạng cần thiết và gây bao phủ thiếu; hãy truyền method="higher" (tương đương "inverted_cdf").
  • Khi hạng cần thiết vượt quá nn (nn nhỏ, mức bao phủ mục tiêu cao), ngưỡng đúng là ++\infty (khoảng = toàn bộ đường thẳng), không phải kẹp về phần dư lớn nhất. Việc kẹp âm thầm phá vỡ bảo đảm 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))

Ghép lại với nhau: vòng lặp 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

Đánh giá kết quả

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

Những cân nhắc thực tiễn

Chọn điểm bất phù hợp

Phần dư tuyệt đối Yμ^(X)|Y - \hat{\mu}(X)| là mặc định, nhưng nó giả định rằng khoảng dự báo nên đối xứng quanh dự báo điểm. Với lợi suất tài chính, các khoảng bất đối xứng thường hợp lý hơn:

  • Conformalized Quantile Regression (CQR): Khớp các bộ hồi quy phân vị ở các mức α/2\alpha/21α/21 - \alpha/2, rồi conformalize (Romano et al., 2019). Các khoảng thích nghi hình dạng của chúng theo phân phối cục bộ — rộng hơn ở phía giảm trong các đợt sụt giảm (drawdown), rộng hơn ở phía tăng trong các đợt tăng giá (rally). (Với CQR khoảng không còn đối xứng nữa, nên wtw_t là độ rộng thực sự bằng cận-trên trừ cận-dưới — hãy tiếp tục dùng wtw_t làm mẫu số ở mọi nơi.)
  • Điểm số chuẩn hóa: Ri=Yiμ^(Xi)/σ^(Xi)R_i = |Y_i - \hat{\mu}(X_i)| / \hat{\sigma}(X_i), trong đó σ^\hat{\sigma} là một ước lượng biến động cục bộ. Điều này tạo ra các khoảng thích nghi theo điều kiện — chặt hơn trong các chế độ biến động thấp, rộng hơn trong các chế độ biến động cao — trong khi vẫn duy trì mức bao phủ biên.

Cỡ tập hiệu chỉnh

Bảo đảm mức bao phủ của split conformal đúng với cỡ tập hiệu chỉnh nn bất kỳ, nhưng độ rộng khoảng giảm khi nn tăng. Với nn rất nhỏ, thống kê thứ tự cần thiết có thể không tồn tại, trong trường hợp đó ngưỡng trung thực là ++\infty (một khoảng vô bổ nhưng hợp lệ) — kẹp về phần dư lớn nhất sẽ âm thầm làm vô hiệu bảo đảm. Với giao dịch thực tế:

  • n100n \geq 100 điểm hiệu chỉnh cho các khoảng chặt một cách hợp lý.
  • n500n \geq 500 được ưu tiên để ước lượng phân vị ổn định.
  • Với ACI, tập hiệu chỉnh chỉ dùng để khởi tạo; các cập nhật mức trực tuyến lo phần còn lại.

Tần suất huấn luyện lại

Mô hình cơ sở μ^\hat{\mu} có thể trở nên lỗi thời. Hai cách tiếp cận:

  1. Huấn luyện lại định kỳ (ví dụ, hàng tháng) và hiệu chỉnh lại phân vị conformal.
  2. Dùng ACI và để mức thích nghi bù đắp cho sự lỗi thời của mô hình — các khoảng tự động nới rộng khi phần dư của mô hình tăng lên.

Cách 2 đơn giản hơn và hiệu quả đến mức bất ngờ. Lớp conformal hoạt động như một lưới an toàn: ngay cả khi mô hình suy thoái, tần suất bao phủ ACI trong dài hạn vẫn hội tụ về mục tiêu.

Chi phí giao dịch

Các khoảng conformal tương tác với chi phí giao dịch theo một cách hữu ích. Khi các khoảng rộng (độ bất định cao), các vị thế nhỏ, nên vòng quay giao dịch (turnover) thấp. Khi các khoảng thu hẹp (mô hình tự tin), các vị thế lớn lên — nhưng mô hình nhiều khả năng đúng hơn, nên vòng quay đáng để trả chi phí.

Bạn cũng có thể đưa chi phí giao dịch trực tiếp vào bộ lọc không-giao-dịch:

chỉ giao dịch neˆˊμ^(Xt)cost>θwt\text{chỉ giao dịch nếu } |\hat{\mu}(X_t)| - \text{cost} > \theta \cdot w_t

Điều này đảm bảo bạn chỉ giao dịch khi biên lợi ròng kỳ vọng vượt một ngưỡng được tỷ lệ theo độ rộng conformal — dùng cùng mẫu số wtw_t như ở mọi nơi khác.

So sánh với các phương pháp truyền thống

Tính chất Khoảng tin cậy Gauss Khoảng tin cậy Bootstrap Khoảng dự báo Conformal
Giả định phân phối Sai số chuẩn (normal) i.i.d. + tiệm cận Không (tính hoán đổi được)
Bảo đảm trên mẫu hữu hạn Không Không
Hoạt động với mọi mô hình Không
Thích nghi với phương sai thay đổi Không Một phần Với CQR / điểm số chuẩn hóa
Xử lý dịch chuyển phân phối Không Không Biến thể ACI / EnbPI
Chi phí tính toán Thấp Trung bình Split: thấp; jackknife+: O(n)O(n) lần khớp lại; full: không khả thi

Bootstrap "tiệm cận" chỉ ở bảo đảm của nó; nó vẫn giả định dữ liệu i.i.d./hoán đổi được và tính trơn, nên ô giả-định-phân-phối của nó không phải là "không-giả-định." Và cột "conformal" đơn lẻ che giấu các chi phí rất khác nhau giữa các biến thể, điều mà hàng chi phí giờ đã nêu rõ.

Hạn chế

Conformal prediction không phải phép màu. Những hạn chế chính với giao dịch:

  1. Bao phủ biên, không phải bao phủ có điều kiện. Bảo đảm là P(Yn+1C(Xn+1))1α\mathbb{P}(Y_{n+1} \in C(X_{n+1})) \geq 1 - \alpha theo nghĩa biên — lấy trung bình trên tính ngẫu nhiên của cả XX lẫn YY. Với một giá trị điều kiện cụ thể X=xX = x, mức bao phủ cục bộ có thể cao hơn hoặc thấp hơn 1α1 - \alpha. Conformalized quantile regression giải quyết điều này một phần.

  2. Tính hoán đổi được là một yêu cầu có thật. Với split conformal, dữ liệu hiệu chỉnh và điểm kiểm tra phải hoán đổi được. Dữ liệu tài chính thì không. ACI và EnbPI nới lỏng điều này thành một bảo đảm dài hạn, nhưng mức bao phủ ngắn hạn có thể lệch đi.

  3. Độ rộng khoảng không phải một mật độ xác suất. Một khoảng conformal cho bạn biết YY sẽ rơi vào đâu với xác suất 1α1-\alpha, nhưng nó không nói gì về phân phối bên trong khoảng. Nó không phải vật thay thế cho một phân phối dự báo đầy đủ — đó chính xác là lý do vì sao suy ra một ppbb của Kelly từ nó đòi hỏi một giả định bổ sung, tường minh.

  4. Rác vào, rộng ra. Một mô hình cơ sở tồi tạo ra các khoảng rộng. Conformal prediction bảo đảm mức bao phủ chứ không bảo đảm tính hữu dụng. Nếu mô hình của bạn không có sức mạnh dự báo, các khoảng sẽ rộng đến mức bộ định cỡ vị thế không bao giờ giao dịch.

Tóm tắt

Conformal prediction cung cấp một khung chặt chẽ, không phụ thuộc phân phối để định lượng độ bất định, một khung tự nhiên ăn khớp với việc định cỡ vị thế:

  • Split conformal cho việc hiệu chỉnh tĩnh, ngoại tuyến với mức bao phủ trên mẫu hữu hạn.
  • Jackknife+ khi dữ liệu hiệu chỉnh khan hiếm và bạn muốn dùng toàn bộ quan sát (với cái giá là nn lần khớp lại).
  • Adaptive conformal inference / EnbPI cho giao dịch trực tuyến với các thị trường không dừng.
  • Định cỡ vị thế qua nghịch đảo độ rộng khoảng và một bộ lọc không-giao-dịch theo tỷ số biên lợi — và, nếu bạn đi tới Kelly, chỉ sau khi suy ra ppbb một cách trung thực, không qua một bộ nhân vô căn cứ.

Lợi thế then chốt so với các lựa chọn tham số: bạn không bao giờ cần đặc tả hay kiểm chứng một giả định phân phối tham số. Các khoảng trung thực theo cấu trúc (theo nghĩa biên, dưới giả định hoán đổi được). Với một nhà giao dịch hệ thống, điều này có nghĩa là bớt đi một nguồn rủi ro mô hình — và trong một ngành mà rủi ro mô hình mang tính sống còn, điều đó quan trọng.


Tài liệu tham khảo:

  • 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.
Tuyên bố miễn trừ trách nhiệm: Thông tin được cung cấp trong bài viết này chỉ nhằm mục đích giáo dục và thông tin, không cấu thành lời khuyên về tài chính, đầu tư hoặc giao dịch. Giao dịch tiền mã hóa tiềm ẩn rủi ro thua lỗ đáng kể.

Tác Giả

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

Đi Trước Thị Trường

Đăng ký nhận bản tin của chúng tôi để có những thông tin chuyên sâu độc quyền về AI trading, phân tích thị trường và các cập nhật nền tảng.

Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất kỳ lúc nào.