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

Cổng Fidelity: Backtest Từ Thô Đến Tinh Đánh Lừa Bạn Nhanh Hơn Trừ Khi Proxy Rẻ Xếp Hạng Giống Đánh Giá Đắt Tiền

Cổng Fidelity: Backtest Từ Thô Đến Tinh Đánh Lừa Bạn Nhanh Hơn Trừ Khi Proxy Rẻ Xếp Hạng Giống Đánh Giá Đắt Tiền
#algotrading
#backtest
#đa fidelity
#hyperband
#ASHA
#giảm một nửa liên tiếp
#quá khớp
#kiểm định
Part 6 of 10 · Collection
High-Performance Backtest Engines

Một phần của loạt bài "Backtest Không Ảo Tưởng".

Mọi tìm kiếm tham số nghiêm túc đều va vào cùng một bức tường: không gian tham số quá lớn, và mỗi lần đánh giá trung thực lại tốn kém. Một chiến lược multi-timeframe với hàng chục ngưỡng và ba chu kỳ chỉ báo, được đánh giá trên một walk-forward split nhiều fold, có thể tốn vài giây cho mỗi cấu hình. Mười nghìn cấu hình là hàng giờ đồng hồ. Vì vậy bạn tìm một lối tắt, và lối tắt luôn là cùng một ý tưởng: đánh giá rẻ, chỉ đưa những cấu hình sống sót lên bài kiểm tra đắt đỏ. Sàng lọc mười nghìn cấu hình trên một fold, giữ lại vài trăm cấu hình tốt nhất, rồi chạy chúng trên tất cả các fold. Sàng lọc trên nến hàng giờ, chỉ đào sâu xuống nến 1 giây cho các ứng viên chung kết.

Đây là tìm kiếm từ thô đến tinh — drill-down, đa fidelity, successive halving, Hyperband, ASHA — và nó là một trong những ý tưởng thực sự tốt trong tối ưu hóa. Nó có thể biến một ngày tính toán thành một giờ. Chúng tôi sử dụng nó. Nhưng nó ẩn chứa một cái bẫy âm thầm đến mức hầu hết những người triển khai nó chưa bao giờ kiểm tra, và cái bẫy đó là: toàn bộ phương pháp giả định rằng proxy rẻ của bạn xếp hạng các cấu hình giống hệt như cách đánh giá đắt đỏ làm. Nếu không đúng như vậy, bạn không tìm kiếm nhanh hơn. Bạn đang vứt bỏ những người chiến thắng tương lai của mình ngay tại cổng đầu tiên và đưa nhiễu lên cao — bạn chỉ đang đánh lừa chính mình nhanh hơn mà thôi.

Bài viết này nói về phép đo duy nhất cho bạn biết drill-down của bạn là thật hay chỉ là ảo tưởng. Chúng tôi đã chạy nó trên tìm kiếm multi-timeframe của chính mình, và câu trả lời thật khó chịu: ở fidelity rẻ nhất, proxy của chúng tôi xếp hạng các cấu hình gần như ngẫu nhiên (Spearman ρ ≈ 0.03). Nếu chúng tôi cắt tỉa mạnh tay ở fidelity đó — như mọi cấu hình mặc định ASHA có sẵn vẫn mời gọi bạn làm — chúng tôi đã loại bỏ những người chiến thắng cuối cùng ngay từ vòng đầu tiên. Cách khắc phục là một cổng bắt buộc chạy trước khi tìm kiếm: đo tương quan thứ hạng, và từ chối cắt tỉa dưới mức fidelity mà tại đó nó còn đáng tin cậy.

Tìm kiếm đa fidelity thực chất là gì

Một chuỗi tìm kiếm từ thô đến tinh: hàng nghìn cấu hình fidelity thấp, giá rẻ được sàng lọc rộng ở bên trái, chỉ những cấu hình sống sót tốt nhất được đưa sang phải qua các lần đánh giá đầy đủ ngày càng đắt đỏ hơn — successive halving thu hẹp một trường ứng viên xanh lục rộng lớn xuống còn vài ứng viên chung kết sáng chói

Cả họ phương pháp này đều có chung một hình dạng. Bạn định nghĩa một fidelity (còn gọi là resource — tài nguyên, hay budget — ngân sách): một núm vặn từ rẻ đến đắt mà ở mức tối đa của nó sẽ cho ra giá trị mục tiêu thật. Sau đó bạn chạy các cấu hình ở fidelity thấp, xếp hạng chúng, giữ lại phần tốt nhất, và chỉ chạy lại những cấu hình sống sót đó ở fidelity cao hơn — lặp lại cho đến khi một số lượng nhỏ ứng viên chung kết được đánh giá ở fidelity đầy đủ.

Successive halving (Jamieson & Talwalkar, 2016) là cốt lõi: bắt đầu N cấu hình ở mức tài nguyên tối thiểu, giữ lại top 1/η, nhân tài nguyên lên η lần, rồi lặp lại. Hyperband (Li et al., 2018) bọc successive halving trong một vòng lặp ngoài để phòng ngừa rủi ro trên nhiều mức tài nguyên khởi đầu khác nhau, để bạn không phải đoán xem nên cắt tỉa mạnh tay đến đâu. ASHA (Li et al., 2020) là phiên bản bất đồng bộ, thân thiện với chạy song song. Tỷ lệ loại bỏ η là núm vặn duy nhất mà chúng có chung; giá trị mặc định kinh điển là η = 3 — giữ lại một phần ba tốt nhất ở mỗi bậc, nhân ba ngân sách.

Bản thân fidelity gần như có thể là bất kỳ thứ gì đi từ rẻ đến đắt và hội tụ về sự thật:

  • Số lượng fold walk-forward. Đánh giá trên 1 fold, rồi 2, rồi 3, cho đến K. Đây là fidelity mà chúng ta nghiên cứu bên dưới, vì nó là loại tự nhiên nhất cho một backtest với walk-forward split kiểu rolling out-of-sample.
  • Độ phân giải nến. Sàng lọc trên nến 1 giờ, đưa lên nến 1 phút, đào sâu xuống nến 1 giây hoặc giao dịch thô chỉ cho các ứng viên chung kết. Đây chính là ý tưởng đào sâu thích ứng được áp dụng cho tìm kiếm thay vì cho mô phỏng khớp lệnh.
  • Độ dài lịch sử, số epoch, tập con dữ liệu — những fidelity kinh điển trong ML.

Trong một backtest, fold và độ phân giải là hai thứ quan trọng, và — đây chính là trọng tâm của cả bài viết — chúng không an toàn như nhau. Một trong hai thứ mang một thiên lệch (bias) âm thầm chọn sai tham số dù bạn có gate cẩn thận đến đâu.

Giả định duy nhất mà mọi thứ đều dựa vào

Sự sụp đổ âm thầm của drill-down: một xếp hạng fidelity thấp, giá rẻ không khớp với xếp hạng đầy đủ đắt đỏ, khiến những người chiến thắng thật sự màu xanh lục bị cắt tỉa ngay tại cổng đầu tiên trong khi nhiễu màu đỏ cam lại được đưa lên cao — và không một lỗi nào từng được báo

Hãy viết ra chính xác điều mà successive halving cần để đúng đắn. Nó không cần fidelity rẻ cho ra đúng giá trị mục tiêu — chẳng ai quan tâm việc một fold báo cáo Sharpe khác với sáu fold. Nó cần một điều yếu hơn nhưng cụ thể hơn nhiều: fidelity rẻ phải xếp hạng các cấu hình giống hệt cách fidelity đắt làm. Nếu cấu hình A thắng cấu hình B ở fidelity đầy đủ, thì A cũng nên có xu hướng thắng B ở fidelity thấp. Chỉ vậy thôi. Và đó cũng là tất cả.

Về mặt hình thức, đại lượng quyết định drill-down có hoạt động hay không là tương quan thứ hạng giữa mục tiêu ở fidelity thấp và fidelity cao, trên toàn bộ không gian cấu hình. Nếu tương quan đó cao, những cấu hình sống sót ở vòng rẻ chính là những cấu hình lẽ ra sẽ thắng ở vòng đắt, và bạn đã tiết kiệm được tính toán miễn phí. Nếu tương quan thấp, vòng rẻ chỉ là một bộ lọc ngẫu nhiên: nó loại bỏ các cấu hình tốt và đưa lên các cấu hình tồi, và bạn đã tiêu tốn ngân sách của mình để tăng tốc đi đến một câu trả lời tệ hơn. Không có mức η trung gian nào cứu được bạn — một proxy tồi được cắt tỉa nhẹ nhàng vẫn để lọt người chiến thắng; cắt tỉa mạnh tay thì nó làm mất gần hết.

Thất bại này vô hình theo cách thường thấy. Tìm kiếm hoàn tất, báo cáo một nhà vô địch, nhà vô địch đó trông ổn trong mẫu (in-sample). Không có lỗi nào được báo. Bạn chỉ phát hiện ra proxy đã nói dối khi nhà vô địch sụp đổ ngoài mẫu (out-of-sample) — và lúc đó bạn đã quy thất bại cho chiến lược, chứ không phải cho tìm kiếm đã chọn ra nó. Vì vậy kỷ luật ở đây vẫn là điều mà loạt bài này liên tục quay lại: đo lường điều mà phương pháp giả định, trước khi bạn tin tưởng phương pháp đó. Với drill-down, điều cần đo là tương quan thứ hạng, và phép đo đó rất rẻ.

Đo lường nó: cổng fidelity

Cổng fidelity như một điểm kiểm tra: trước khi bất kỳ bậc nào được tin tưởng, khoảng hai trăm cấu hình ngẫu nhiên được chấm điểm ở mọi mức fidelity và tương quan thứ hạng giữa proxy rẻ và đánh giá đầy đủ được đo tại một cánh cổng phát sáng chỉ mở ra khi hai bảng xếp hạng đồng thuận với nhau

Cổng này là một thử nghiệm nhỏ mà bạn chạy một lần, trước khi tìm kiếm thật sự bắt đầu, trên chính không gian mà bạn sắp tìm kiếm. Quy trình như sau:

  1. Rút ngẫu nhiên vài trăm cấu hình từ không gian tham số (chúng tôi dùng ~200 — đủ để có một ước lượng Spearman ổn định, đủ rẻ để có thể chi trả).
  2. Đánh giá từng cấu hình ở mọi bậc fidelity: ở 1 fold, 2 fold, … cho đến đủ K fold. Với fidelity theo số fold, việc này gần như miễn phí, vì các fold bạn tính cho các bậc rẻ được tái sử dụng ở bậc đắt hơn — một trung bình tích lũy.
  3. Với mỗi bậc r, tính tương quan thứ hạng Spearman giữa xếp hạng r-fold và xếp hạng đầy đủ K-fold, trên khoảng 200 cấu hình đó.
  4. Bậc đầu tiên mà ρ vượt qua một ngưỡng (chúng tôi dùng ρ ≥ 0.5) là fidelity nông nhất mà bạn được phép cắt tỉa. Dưới bậc đó, xếp hạng quá nhiễu để tin tưởng; bạn không được phép loại bỏ cấu hình ở đó.

Toàn bộ chỉ là vài chục dòng code. Phần cốt lõi của nó:

def fidelity_check(cache, n_probe, seed=7):
    """Spearman ρ: cumulative mean over the first r folds (in FOLD_ORDER)
    vs the full K-fold objective, on n_probe random configs."""
    rng = np.random.default_rng(seed)
    k = len(FOLDS)
    per_fold = np.empty((k, n_probe))
    order_win = np.array([list(FOLDS[fi]) for fi in FOLD_ORDER], np.int64)
    for j in range(n_probe):
        p = _unit_to_params(rng.random(len(PNAMES)))       # random config
        scores, *_ = eval_group(cache, p, _sp_of(p)[None, :], order_win, False)
        per_fold[:, j] = scores[0]                          # score on each fold
    cums = np.cumsum(per_fold, axis=0) / np.arange(1, k + 1)[:, None]  # r-fold mean
    rhos = []
    for r in range(1, k):
        rho = spearmanr(cums[r - 1], cums[-1]).statistic    # r folds vs all K
        rhos.append(0.0 if math.isnan(rho) else rho)
    return rhos

Lưu ý một chi tiết có chủ đích: các fold được lấy theo FOLD_ORDER, một thứ tự xen kẽ (interleaved) luân phiên giữa các lát cắt sớm và muộn trong lịch (fold 0, rồi một fold ở giữa, rồi fold 1, rồi một fold muộn hơn, …). Điều này quan trọng đến mức đáng kể và sẽ là chủ đề của một phần sau: nó có nghĩa là "1 fold" là một lát cắt trải giữa lịch sử, và "2 fold" là một lát sớm cộng một lát muộn — không bao giờ là một cửa sổ gần đây liên tục. Fidelity rẻ trở nên rẻ hơn bằng cách dùng ít fold hơn, chứ không phải bằng cách dùng các fold gần đây hơn.

Kết quả: một fold xếp hạng gần như ngẫu nhiên

Tương quan thứ hạng tăng dần theo số fold: một đường cong Spearman rho tăng dần, bắt đầu gần như phân tán kiểu tung đồng xu ở một fold rồi dốc lên gần như đồng thuận hoàn hảo ở năm fold, với một đường ngang biểu thị cổng được vẽ ngang qua điểm giữa, dưới đó cắt tỉa bị cấm và trên đó thì được phép

Đây là những gì cổng báo cáo, chạy trên hai thiết lập multi-timeframe của chúng tôi. Con số là Spearman ρ giữa xếp hạng r-fold và xếp hạng đầy đủ K-fold trên khoảng 200 cấu hình ngẫu nhiên — cho biết xếp hạng ở fidelity rẻ dự đoán trung thực đến đâu xếp hạng ở fidelity đầy đủ.

Số fold r Lượt chạy Multi-TF Một regime khó hơn
1 +0.43 +0.03
2 +0.67 +0.43
3 +0.78 +0.78
4 +0.82
5 +0.91 +0.91

Hãy đọc cột regime-khó-hơn trước, vì đó là cột đáng báo động. Ở một fold, ρ = 0.03. Đó không phải là một tương quan yếu; đó là không có tương quan gì cả — xếp hạng một-fold của hai trăm cấu hình về mặt thống kê không thể phân biệt được với việc xáo trộn ngẫu nhiên chúng. Một lượt chạy successive halving được cấu hình (như hầu hết đều vậy, theo mặc định) để bắt đầu cắt tỉa ở min_resource = 1 sẽ, trong regime này, thực hiện cú cắt đầu tiên và mạnh tay nhất của nó dựa trên một cú tung đồng xu. Những cấu hình rốt cuộc sẽ thắng ở fidelity đầy đủ chỉ có 50/50 cơ hội sống sót qua vòng đầu tiên. Đến khi bạn đến được fidelity mà xếp hạng có ý nghĩa (ρ = 0.78 ở ba fold), hầu hết chúng đã biến mất.

Cột multi-TF nhẹ nhàng hơn nhưng vẫn nói lên cùng một điều theo một cách khác. Ngay cả ở đó, một fold cho ρ = 0.43 — dưới ngưỡng cổng 0.5 của chúng tôi. Nó trông giống như một tương quan khá tốt, và đó chính xác là điều nguy hiểm: 0.43 đủ cao để ru ngủ bạn và đủ thấp để làm lọt mất những cấu hình tốt nhất của bạn. Chỉ ở hai fold (ρ = 0.67) xếp hạng mới trở nên đáng tin cậy.

Hai điều được khái quát hóa từ đây. Thứ nhất, ρ ở một fold phụ thuộc vào regime và không đáng tin cậy — chúng tôi đo được từ 0.03 đến 0.43 tùy thiết lập, và ở cả hai trường hợp, một fold duy nhất đều không vượt qua ngưỡng. Thứ hai, ρ tăng đơn điệu và nhanh: đến ba fold cả hai thiết lập đều đạt 0.78, và đến năm fold chúng hội tụ về 0.91. Tín hiệu vẫn ở đó; bạn chỉ cần bỏ ra đủ fidelity để thấy nó. Nhiệm vụ của cổng là tìm ra chính xác bậc mà tại đó "đủ" bắt đầu — và cấm cắt tỉa dưới bậc đó.

Vì sao một fold đơn lẻ lại nhiễu đến vậy

Nhiễu này không phải là một lỗi trong cách chúng tôi xây dựng fold; nó là bản chất nội tại, và hiểu được lý do sẽ giúp bạn tránh "sửa" nó theo cách sai. Một fold walk-forward đơn lẻ là một cửa sổ ngắn — vài tuần của một regime thị trường. Điểm số của một chiến lược trên cửa sổ đó bị chi phối chủ yếu bởi các tham số của nó tình cờ khớp với regime đó tốt đến mức nào, điều này chỉ liên quan lỏng lẻo đến việc chúng tổng quát hóa tốt đến đâu. Hai cấu hình thực sự khác nhau về chất lượng có thể đổi chỗ cho nhau trên một fold đơn lẻ chỉ vì một trong hai bắt được một xu hướng mà fold đó tình cờ chứa. Giá trị mục tiêu trên một fold là một bộ ước lượng có phương sai cao của giá trị mục tiêu mà bạn thực sự quan tâm, và tương quan thứ hạng chính là thứ mà phương sai cao phá hủy đầu tiên — bạn có thể giữ nguyên giá trị trung bình của một bộ ước lượng trong khi xếp hạng của nó lại là nhiễu thuần túy.

Thêm fold vào sẽ trung bình hóa phương sai đó xuống. Mỗi fold là một lần rút mẫu gần như độc lập của điều kiện thị trường; trung bình r-fold là một bộ ước lượng có phương sai thấp hơn, và xếp hạng của nó hội tụ về phía xếp hạng ở fidelity đầy đủ. Đó chính xác là sự leo thang ρ = 0.03 → 0.43 → 0.78 → 0.91: không phải giá trị mục tiêu thay đổi, mà là ước lượng xếp hạng của nó ổn định dần khi may mắn đặc thù theo regime được trung bình hóa đi. Bài học rút ra là fidelity, đối với một backtest, về bản chất là chuyện bạn đã lấy mẫu bao nhiêu regime độc lập — và một regime thì gần như không bao giờ đủ để xếp hạng.

Điều này cũng giải thích tại sao regime khó hơn bắt đầu ở 0.03 trong khi lượt chạy multi-TF bắt đầu ở 0.43. Trong regime khó hơn, fold đơn lẻ mang tính đặc thù-regime nhiều hơn — điểm số một-fold của các cấu hình bị chi phối nhiều hơn bởi may mắn và ít hơn bởi lợi thế bền vững, nên chúng xếp hạng gần với ngẫu nhiên hơn. Cổng tự động đọc được sự khác biệt đó và phản ứng bằng cách đòi hỏi nhiều fold hơn trước khi cho phép cắt tỉa. Bạn không cần biết trước mình đang ở regime nào; bạn chỉ cần đo.

Cổng trong code: tự động nâng fidelity tối thiểu

Đầu ra của cổng là một số nguyên duy nhất: min_resource, fidelity nông nhất mà ASHA được phép cắt tỉa. Quy tắc mang tính cơ học — đi qua từng bậc, lấy bậc đầu tiên có ρ vượt ngưỡng:

RHO_GATE = 0.5
min_res = len(FOLDS)                      # default: pruning OFF (full fidelity)
rhos = fidelity_check(cache, n_probe=200) # [ρ@1, ρ@2, …, ρ@(K-1)]
passing = [r for r, rho in enumerate(rhos, 1) if rho >= RHO_GATE]
if passing:
    min_res = passing[0]                  # first rung that clears the gate

pruner = SuccessiveHalvingPruner(min_resource=min_res, reduction_factor=3)

Hãy theo dõi điều này qua hai lượt chạy. Trong thiết lập multi-TF, ρ@1 = 0.43 không qua được cổng nhưng ρ@2 = 0.67 thì qua, nên min_resource được tự động nâng lên 2: ASHA chạy mọi cấu hình trên ít nhất hai fold trước khi được phép loại bỏ bất cứ thứ gì, rồi cắt tỉa bình thường từ đó trở đi. Trong regime khó hơn, cả ρ@1 = 0.03 và ρ@2 = 0.43 đều không qua; ρ@3 = 0.78 là bậc đầu tiên qua được, nên min_resource trở thành 3. Và cơ chế dự phòng quan trọng: nếu không có bậc nào đạt 0.5, passing rỗng và min_resource giữ nguyên ở K — cắt tỉa bị tắt hoàn toàn, và tìm kiếm suy giảm một cách nhẹ nhàng về một tìm kiếm fidelity-đầy-đủ đơn thuần thay vì một tìm kiếm nhanh nhưng sai. Một drill-down không thể chứng minh được proxy của nó thì đơn giản là từ chối cắt tỉa.

Đây là toàn bộ triết lý gói gọn trong một dòng luồng điều khiển. Mặc định của mọi thư viện successive-halving là "cắt tỉa từ min_resource = 1, tin tưởng bậc rẻ." Cổng thay thế điều đó bằng "cắt tỉa từ bậc đầu tiên mà dữ liệu nói là đáng tin cậy, và nếu không có bậc nào như vậy, đừng cắt tỉa." Nó tốn một lần thăm dò ~200 cấu hình từ đầu và nó biến drill-down từ một hành động đức tin thành một quyết định có đo lường. Tỷ lệ loại bỏ η = 3 vẫn giữ nguyên; cổng không đụng đến việc bạn cắt tỉa mạnh tay đến đâu, chỉ đụng đến việc bạn được phép bắt đầu sớm đến đâu.

Một lưu ý trung thực có thể thấy trong đoạn code trên: nâng min_resource sẽ ăn vào mức tăng tốc. Cắt tỉa ở 3 fold thay vì 1 nghĩa là mọi cấu hình — kể cả những cấu hình bạn sẽ loại bỏ — đều phải trả giá cho ba fold. Đó là cái giá của sự đúng đắn, và đó là một sự đánh đổi hợp lý: một mức tăng tốc nhỏ hơn 2 lần trên một tìm kiếm tìm ra người chiến thắng thật sự vẫn tốt hơn một mức tăng tốc 6 lần trên một tìm kiếm loại bỏ mất họ. Cổng thực hiện sự đánh đổi đó một cách tường minh thay vì che giấu nó.

Trục rẻ sai lầm: lịch sử ngắn hơn là một cái bẫy

Có một fidelity rẻ vừa hấp dẫn, vừa hiển nhiên, vừa thiên lệch (biased), và đáng để gọi tên nó ra vì ai cũng tìm đến nó đầu tiên: lịch sử ngắn hơn. Sàng lọc các cấu hình trên tháng gần nhất, đưa những cấu hình sống sót lên đánh giá trên đầy đủ hai năm. Nó rẻ một cách hiển nhiên và cảm giác giống hệt ý tưởng ít fold hơn. Nhưng không phải vậy.

Ít fold hơn và lịch sử ngắn hơn khác nhau ở một điểm mang tính quyết định. Một fidelity theo số fold, nếu làm đúng, vẫn trải khắp toàn bộ lịch — nó chỉ lấy mẫu thô hơn thôi. Một fidelity theo lịch sử ngắn hơn lấy mẫu dày đặc trên một khoảng con của lịch. Và một khoảng con của lịch sử thị trường chính là một regime cụ thể. Khi bạn xếp hạng các cấu hình trên tháng gần nhất, bạn không nhận được một ước lượng nhiễu-nhưng-không-thiên-lệch về xếp hạng trên toàn kỳ của chúng; bạn nhận được một ước lượng thiên lệch một cách hệ thống ưu ái các cấu hình được tinh chỉnh cho regime của tháng gần nhất. Bạn có tăng cỡ mẫu lên bao nhiêu tùy thích — trung bình hóa trên nhiều cấu hình hơn, nhiều lần thử hơn — thì thiên lệch đó cũng không thu nhỏ lại, vì nó không phải là phương sai. Bạn sẽ đưa lên những cấu hình khớp tốt nhất với cửa sổ sàng lọc, và đó chính xác là những cấu hình nhiều khả năng nhất bị quá khớp với một regime nhất thời.

Đây là lý do fidelity của chúng tôi đi qua các fold theo thứ tự xen kẽ thay vì theo thời gian. Với K fold trải khắp toàn bộ lịch sử, "1 fold" là một lát cắt đơn lẻ gần giữa, "2 fold" là một lát sớm và một lát muộn, "3 fold" trải ra sớm/giữa/muộn — mọi mức fidelity, kể cả mức rẻ nhất, đều lấy mẫu trên toàn bộ lịch. Proxy rẻ là một cái nhìn thô về toàn bộ giai đoạn, không bao giờ là một cái nhìn sắc nét vào một lát cắt của nó. Chính sự xen kẽ đó khiến fidelity theo số fold chỉ đơn thuần nhiễu (có thể chữa được bằng cổng) thay vì thiên lệch (mà không cổng nào chữa được — một ρ cao so với một mục tiêu thiên lệch chỉ xác nhận rằng bạn sẽ luôn chọn ra cấu hình khớp-regime một cách đáng tin cậy). Nếu cách duy nhất để làm cho một fidelity rẻ hơn là làm cho nó gần đây hơn, thì bạn không có một fidelity hợp lệ. Hãy làm cho nó thô hơn thay vào đó.

Độ phân giải như trục rẻ trung thực

Trục rẻ còn lại tránh được thiên lệch hoàn toàn, và nó là đối tác tự nhiên của đào sâu thích ứng mà chúng tôi đã mô tả cho mô phỏng khớp lệnh: độ phân giải nến. Sàng lọc toàn bộ không gian trên nến 1 giờ, đưa những cấu hình sống sót lên nến 1 phút, và đào sâu xuống nến 1 giây hoặc giao dịch thô chỉ cho một số ít ứng viên chung kết. Nến thô hơn trên cùng một lịch sử đầy đủ thì rẻ hơn để đánh giá — ít nến hơn, các lượt tính chỉ báo nhanh hơn, mô phỏng nhanh hơn — và, không giống một cửa sổ ngắn hơn, một cái nhìn thô về toàn bộ lịch là không thiên lệch: nó thấy được mọi regime, chỉ là với ít chi tiết trong-nến (intrabar) hơn.

Độ phân giải và fold là hai trục fidelity bổ sung cho nhau, và cổng fidelity áp dụng cho cả hai. Trước khi tin tưởng một màn sàng lọc 1 giờ, hãy chạy cùng một phép thăm dò: lấy ~200 cấu hình, chấm điểm chúng ở 1h và ở 1m, rồi đo tương quan thứ hạng. Nếu ρ(1h, 1m) cao, sàng lọc theo giờ là an toàn và bạn đã mua được một mức tăng tốc lớn một cách trung thực. Nếu nó thấp — điều xảy ra khi lợi thế của chiến lược nằm trong cấu trúc trong-nến mà nến hàng giờ xóa nhòa đi — thì sàng lọc theo giờ là một bộ lọc ngẫu nhiên đối với chiến lược đó, và cổng sẽ báo cho bạn bắt đầu ở độ phân giải mịn hơn. Quy tắc không bao giờ thay đổi: bạn chỉ được phép cắt tỉa ở một fidelity sau khi bạn đã đo được rằng xếp hạng của nó khớp với sự thật.

Hai trục này cũng thất bại theo những cách đối nghịch nhau, và điều đó lại hữu ích. Độ phân giải thô làm mất thông tin trong-nến; ít fold hơn làm mất thông tin xuyên-regime. Một chiến lược momentum trên khung thời gian ngày có thể xếp hạng hoàn hảo trên nến hàng giờ nhưng cần nhiều fold để trung bình hóa may mắn theo regime; một chiến lược scalping có thể xếp hạng tốt trên ít fold nhưng sụp đổ khi lên trên độ phân giải 1 giây. Cổng, khi chạy theo từng trục, cho bạn biết fidelity nào bạn có thể tiết kiệm được cho chiến lược này — thay vì giả định một câu trả lời chỉ vì nó tiện lợi.

Mức tiết kiệm thực sự đến từ đâu

Một lưu ý giữ cho drill-down trung thực: một fidelity chỉ tiết kiệm tính toán ở nơi chi phí thực sự nằm. Trong engine multi-timeframe của chúng tôi, phần đắt đỏ là tính trước các chỉ báo — HMA multi-timeframe và các tín hiệu phân tách — được trả một lần cho mỗi cấu hình, trước khi bất kỳ fold nào chạy. Mô phỏng theo từng fold trên các tín hiệu đã cache tương đối rẻ. Vì vậy cắt tỉa theo số fold chỉ tiết kiệm chi phí mô phỏng, chứ không tiết kiệm chi phí chỉ báo vốn chiếm phần lớn; fidelity theo số fold là có thật nhưng trần của nó thấp hơn so với tỷ lệ fold thô gợi ý.

Ngược lại, trục độ phân giải tấn công trực tiếp vào chi phí chiếm ưu thế: nến thô hơn nghĩa là ít nến hơn để tính chỉ báo trên đó, nên cả phần tính trước đắt đỏ lẫn phần mô phỏng rẻ đều co lại cùng nhau. Đây không phải là một chi tiết vặt vãnh — nó quyết định drill-down nào đáng để xây dựng. Trước khi đầu tư vào một tìm kiếm đa fidelity, hãy hỏi thời gian của bạn đi về đâu. Nếu 90% là tính trước chỉ báo được chia sẻ giữa các fold, một fidelity theo số fold mang lại rất ít, còn một fidelity theo độ phân giải mang lại rất nhiều. Hãy profile trước; trục rẻ đúng đắn là trục loại bỏ được chi phí mà bạn thực sự có, và nó vẫn phải vượt qua được cổng.

Nó kết nối với những gì

Cổng fidelity nằm ở một vị trí cụ thể trong chuỗi vệ sinh backtest của loạt bài này:

  • nằm ở thượng nguồn của việc kiểm soát quá khớp. Một drill-down cắt tỉa dựa trên một proxy tồi là một cách mới để quá khớp — bạn đang để một bậc sớm đầy nhiễu chọn ra các ứng viên chung kết của mình. Nhà vô địch vẫn phải sống sót qua Deflated Sharpe và PBO, và số lượng lần thử đưa vào các cổng đó phải bao gồm mọi lần thử đã bị cắt tỉa, chứ không chỉ những cấu hình sống sót — cắt tỉa không khiến một lần thử được miễn trừ khỏi sổ sách kiểm định đa giả thuyết.
  • Nó có chung kẻ thù với phân tích plateau: một cấu hình thắng ở một fold hoặc một cửa sổ sàng lọc và không thắng ở đâu khác chính là cùng một hiện vật khớp-regime (regime-fit artifact) mà cả hai công cụ đều tồn tại để loại bỏ. Cổng từ chối chọn trên một fold như vậy; phân tích plateau từ chối tin tưởng một nhà vô địch chỉ đứng vững trên một fold.
  • Nó giả định bên dưới có một walk-forward split trung thực — các fold trải khắp toàn bộ lịch, phần out-of-sample được giữ riêng — và nó là phần bổ sung ở thời điểm tìm kiếm cho drill-down mà chúng tôi đã xây dựng cho mô phỏng khớp lệnh: cùng nguyên lý từ thô đến tinh, nhưng áp dụng cho cấu hình nào cần đánh giá thay vì khớp lệnh chính xác đến đâu.
  • Và nó phụ thuộc tuyệt đối vào việc fidelity không bị rò rỉ ở mọi bậc. Nếu bậc rẻ có thiên lệch nhìn trước (look-ahead bias) mà bậc đắt không có, ρ đo sự đồng thuận với một mục tiêu đã bị nhiễm bẩn và cổng chứng thực cho một rò rỉ. Đo tương quan thứ hạng, đúng vậy — nhưng trên một mục tiêu trung thực.

Ý tưởng thống nhất, một lần nữa, là điều mà loạt bài này sẽ không ngừng nhắc lại: một backtest là một thí nghiệm thống kê, và mọi lối tắt trong đó là một giả thuyết mà bạn có nghĩa vụ phải kiểm định. Giả thuyết của drill-down là "xếp hạng rẻ ≈ xếp hạng đắt." Nó có thể kiểm định được trong khoảng 200 cấu hình. Hãy kiểm định nó.

Tổng kết

  1. Tìm kiếm từ thô đến tinh dựa trên một giả định duy nhất: proxy rẻ xếp hạng các cấu hình giống như proxy đắt. Không phải cùng giá trị — mà là cùng xếp hạng. Nếu tương quan thứ hạng thấp, cắt tỉa mạnh tay sẽ vứt bỏ những người chiến thắng tương lai của bạn và đưa nhiễu lên cao. Bạn tìm kiếm nhanh hơn để đi đến một câu trả lời tệ hơn.
  2. Đo lường trước khi tin tưởng. Rút ~200 cấu hình ngẫu nhiên, chấm điểm chúng ở mọi bậc fidelity, và tính Spearman ρ giữa mỗi bậc rẻ và xếp hạng ở fidelity đầy đủ. Đó chỉ là vài chục dòng code và một phép thăm dò rẻ.
  3. Một fold xếp hạng gần như ngẫu nhiên. Chúng tôi đo được ρ@1 từ 0.03 (một cú tung đồng xu) đến 0.43 (vẫn dưới ngưỡng tin cậy) tùy vào regime; nó leo lên 0.67, 0.78, 0.82, và 0.91 khi số fold tích lũy dần. Mặc định min_resource = 1 của mọi ASHA có sẵn thường là sai, đối với một backtest.
  4. Tự động nâng fidelity tối thiểu lên bậc đầu tiên có ρ ≥ 0.5, và nếu không bậc nào đạt, đừng cắt tỉa gì cả. Cổng biến thành min_resource = 2 cho một thiết lập và 3 cho một thiết lập khó hơn; cơ chế dự phòng suy giảm nhẹ nhàng về tìm kiếm fidelity-đầy-đủ. Sự đúng đắn có giá bằng một phần tốc độ tăng thêm — hãy trả cái giá đó.
  5. Chọn trục rẻ dựa trên việc nó có thiên lệch hay không, chứ không phải dựa trên việc nó rẻ hay không. Lịch sử ngắn hơn bị thiên lệch — nó chọn ra các tham số khớp-regime và không cỡ mẫu nào sửa được điều đó. Hãy dùng ít fold hơn nhưng trải khắp toàn bộ lịch (xen kẽ, không liên tục) hoặc độ phân giải thô hơn trên toàn bộ giai đoạn. Và hãy dùng trục rẻ ở nơi chi phí của bạn thực sự nằm.

Drill-down là một trong những cách tăng tốc tốt nhất trong backtesting, và cũng là một trong những cách dễ nhất để biến thành một phương thức đánh lừa chính mình nhanh hơn. Sự khác biệt giữa hai điều đó là một con số duy nhất mà bạn có thể đo trước khi tìm kiếm bắt đầu. Nếu proxy của bạn không thể chứng minh rằng nó xếp hạng giống như sự thật, thì nó không phải là một proxy — nó là một bộ sinh số ngẫu nhiên với một hồ sơ chi phí có vẻ hợp lý.

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.