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

Không gian tham số hai trục: Vì sao phần lớn phép quét của bạn nên gần như miễn phí

Không gian tham số hai trục: Vì sao phần lớn phép quét của bạn nên gần như miễn phí
#giao dịch thuật toán
#backtest
#tìm kiếm tham số
#tối ưu hóa
#bộ nhớ đệm
#lời nguyền số chiều
Part 4 of 10 · Collection
High-Performance Backtest Engines

Thuộc loạt bài "Backtest không ảo tưởng".

Lời nguyền số chiều thường được kể như một lời cảnh báo: mỗi tham số bạn thêm vào sẽ nhân không gian tìm kiếm lên, nên một chiến lược 18 chiều là vô vọng nếu muốn quét hết. Cách đặt vấn đề đó ngầm giả định mọi chiều đều tốn chi phí đánh giá như nhau. Thực tế không phải vậy. Trong engine hai/ba khung thời gian của chúng tôi, hai phần ba số tham số gần như miễn phí khi quét, còn một phần ba gánh gần trọn hóa đơn tính toán. Một khi bạn nhìn ra sự phân tách đó, "không gian tìm kiếm quá lớn" không còn là lời phàn nàn đúng nữa. Câu hỏi đúng phải là: bạn đang trả tiền cho những chiều nào?

Cái chiều vốn không cần tính lại

Benchmark tìm kiếm tham số của chúng tôi chạy một chiến lược động lượng đa khung thời gian: đường trung bình động Hull và biến thể làm mượt ba lần của nó (HMA3) trên hai hoặc ba khung thời gian, cùng một cổng lọc độ tách có hướng quyết định khi nào một điểm giao cắt đủ "sạch" để hành động. Không gian tìm kiếm đầy đủ của biến thể ba khung (triple-TF) có mười tám chiều — chu kỳ và độ dài HMA cho từng khung thời gian, cộng thêm một bộ ngưỡng độ tách cho vào lệnh và thoát lệnh trên mỗi khung.

Cách quét ngây thơ là một vòng lặp duy nhất: chọn một vector tham số, dựng mọi chỉ báo từ đầu, chạy mô phỏng, chấm điểm, lặp lại. Chúng tôi đã bắt đầu đúng như thế. Nó chậm, và chậm vì một lý do hóa ra khá xấu hổ khi gọi đúng tên: với đại đa số các lần thử liền kề, chúng tôi đang tính lại những chỉ báo vốn không hề thay đổi.

Nhích ngưỡng độ tách vào lệnh từ 0.03 lên 0.035 rồi chạy lại. Đường Hull MA trên một năm nến 1 phút giống hệt lần thử trước đến từng bit — ngưỡng không xuất hiện ở bất kỳ đâu trong định nghĩa của nó. Vậy mà vòng lặp ngây thơ vẫn dựng lại nó, trên hơn nửa triệu nến, sâu ba khung thời gian, lần nào cũng vậy. Chúng tôi đang tiêu gần như toàn bộ ngân sách tính toán để suy ra lại những đại lượng bất biến với chính cái tham số mà chúng tôi đang thay đổi.

Quan sát đó chính là toàn bộ bài viết này. Khoảnh khắc bạn nhận ra một số tham số làm thay đổi chỉ báo còn một số chỉ thay đổi quy tắc quyết định áp lên các chỉ báo cố định, không gian tham số không còn là một đám mây chiều phẳng lì nữa, mà trở thành hai lớp lồng nhau với mức giá chênh lệch một trời một vực.

Hai trục, không phải một

Hai trục của một không gian tham số với mức giá khác nhau: một lưới chỉ báo đắt và thưa, mỗi ô của nó mở ra một tấm lưới cấu hình ngưỡng dày đặc, gần như miễn phí

Hãy phân loại tham số theo việc đánh giá lại chúng buộc bạn phải tính lại những gì:

  • Trục đắt — tham số chỉ báo. Chu kỳ khung thời gian và độ dài HMA. Thay đổi một trong hai và bạn phải dựng lại chỉ báo trên toàn bộ chuỗi giá: tái lấy mẫu khung thời gian cao hơn, chạy phép lấy trọng số Hull, tính lại chuỗi giao cắt và độ tách tại mỗi điểm giao. Đây là một lượt quét O(n) trên từng nến lịch sử, và bạn phải trả đủ cho mỗi tổ hợp riêng biệt. Trong chiến lược ba khung, trục này gồm sáu tham số: (period, hma_length) cho mỗi khung cao, trung và thấp.

  • Trục rẻ — ngưỡng quyết định. Các cổng độ tách có hướng quyết định, dựa trên chuỗi giao cắt và độ tách đã được tính sẵn, việc vào hay thoát lệnh. Thay đổi một ngưỡng và không chỉ báo nào xê dịch. Bạn chỉ cần chạy lại một lượt duyệt O(n) duy nhất trên các tín hiệu đã tính trước, kiểm tra các cổng và ghi nhận khớp lệnh. Trong chiến lược ba khung, trục này gồm mười hai tham số (bốn ngưỡng độ tách — vào mua, vào bán, thoát mua, thoát bán — trên mỗi khung trong ba khung thời gian). Biến thể hai khung có tám (bốn ngưỡng trên mỗi khung trong hai khung).

Vậy hình dạng thật của không gian là: 6 đắt + 12 rẻ = 18 cho ba khung, và 4 đắt + 8 rẻ = 12 cho hai khung. Hai phần ba số chiều nằm trên trục rẻ. Và hai trục không chỉ khác nhau về số lượng — chúng khác nhau về chi phí đơn vị đến hơn ba bậc độ lớn, và đó chính là con số mà phần còn lại của bài viết xoay quanh.

Cấu trúc này lồng nhau một cách chặt chẽ. Mỗi điểm trên trục đắt — một bộ (period, hma_length) cụ thể cho từng khung thời gian — xác định một tập mảng tín hiệu cố định. Trên nền móng cố định đó là cả một tấm lưới của trục rẻ: hàng nghìn vector ngưỡng, mỗi vector là một lượt duyệt nhanh trên cùng những mảng ấy. Bạn trả tiền cho nền móng một lần và khấu hao nó trên toàn bộ tấm lưới.

Vì sao chỉ báo bất biến với ngưỡng

Cơ chế cache chỉ hoạt động nhờ một sự thật toán học, chứ không phải một chi tiết cài đặt may mắn, và đáng để phát biểu thật chính xác vì đây là giả định chịu lực của cả kiến trúc. Các mảng chỉ báo là hàm của duy nhất (period, hma_length). Các ngưỡng độ tách không xuất hiện ở bất kỳ đâu trong định nghĩa của chúng.

Cụ thể, với mỗi khung thời gian, engine tính trước bốn mảng đã căn chỉnh theo chỉ số nến cơ sở: đường Hull MA hma, bản làm mượt ba lần hma3, chuỗi giao cắt cross (+1 mua / −1 bán / 0), và phần trăm separation tại mỗi điểm giao. Tất cả đều được suy ra từ giá và hai tham số chỉ báo. Còn ngưỡng — con số mà bạn đem separation ra so sánh — được áp dụng sau, tại thời điểm ra quyết định. Nó không bao giờ chạm vào các mảng.

Đây không phải một phép sắp xếp lại an toàn hiển nhiên. Nó chỉ an toàn vì chúng tôi đã cẩn trọng với việc thông tin trở nên khả dụng vào lúc nào — mối bận tâm của công trình về thiên lệch nhìn trước (look-ahead bias) ở nơi khác trong loạt bài này. Chỉ báo khung thời gian cao hơn tại nến cơ sở i được tính từ các nến khung cao đã đóng cộng với một nến đang hình thành mà giá đóng cửa tạm thời của nó bằng đúng giá đóng cơ sở hiện tại close[i] — một giá trị đã biết tại nến i. Không có gì từ tương lai rò rỉ vào. Vì thế tín hiệu tính trước tại nến i chính xác là những gì một bot chạy thật sẽ thấy tại nến i, và nó vẫn hợp lệ bất kể sau này bạn đem ngưỡng nào ra kiểm thử với nó. Cache một tín hiệu bị rò rỉ chỉ là cache luôn cả chỗ rò; cache một tín hiệu nhân quả mới là cache thứ bạn thực sự giao dịch được.

Tính bất biến này cho chúng ta một phép phân tách (factorization). Viết phép đánh giá một vector tham số đầy đủ θ = (indicator_params, threshold_params) như sau:

signals   = build_indicators(indicator_params)      # EXPENSIVE, depends only on indicator_params
score     = simulate(signals, threshold_params)     # CHEAP, reuses signals across all thresholds

build_indicators không hề đọc threshold_params. Chính sự thật duy nhất đó là thứ cấp phép cho cache: giữ indicator_params cố định, thay đổi threshold_params thoải mái, và signals là một hằng số bạn chỉ tính một lần.

Kiến trúc: tính một lần, quét nhiều lần

Một cache tín hiệu khóa theo tham số chỉ báo cấp dữ liệu cho phép quét theo lô tốc độ cao: một lần dựng chỉ báo tỏa ra thành một hàng rộng các phép đánh giá ngưỡng dùng chung cùng những mảng đã cache

Engine (scripts/engine_multitf.py, commit bfc8aaa trong backtester của chúng tôi) cài đặt phép phân tách này bằng hai mảnh ghép mà tên gọi nói lên chính xác việc chúng làm.

SignalCache — trục đắt, được memo hóa. Đây là một dictionary khóa theo (period_bars, hma_length). Hỏi nó tín hiệu của một khung thời gian và nó trả về TFSignals đã cache nếu tổ hợp chỉ báo đó từng được dựng trước đây, còn không thì dựng một lần rồi lưu lại. Vì khóa chỉ gồm các tham số chỉ báo, mọi cấu hình ngưỡng dùng chung một tổ hợp chỉ báo — và trong một phép quét ngưỡng dày đặc thì con số đó là hàng nghìn — đều trúng cùng một mục cache. Các khung thời gian cao hưởng lợi đặc biệt từ điều này: một lưới thô gồm, chẳng hạn, bốn chu kỳ ứng viên nhân với một nhúm độ dài HMA chỉ là một số nhỏ các lần dựng đắt đỏ riêng biệt, và mỗi lần dựng được tái sử dụng trên toàn bộ tấm lưới ngưỡng xếp chồng bên trên.

sweep_separations — trục rẻ, chạy theo lô. Nó nhận các mảng tín hiệu đã cache cùng một ma trận vector ngưỡng (sps, kích thước [m, 12]) rồi chạy tất cả qua một kernel đã biên dịch duy nhất. Mỗi hàng là một lượt duyệt O(n): đi qua các nến, áp các cổng, ghi nhận khớp lệnh không rò rỉ tại open[i+1], cộng dồn PnL và thời gian giữ vị thế. Không chỉ báo nào được dựng lại bên trong vòng lặp này — nó đọc crossseparation thẳng từ cache. Phần mô phỏng bên trong được biên dịch JIT (Numba), nên sau khi làm nóng, chi phí mỗi cấu hình bị chi phối bởi một lượt quét tuyến tính duy nhất qua các nến, chứ không phải bởi overhead của Python.

Hai mảnh ghép này hợp thành phép tìm kiếm lồng nhau tự nhiên: vòng lặp ngoài đi dọc trục đắt (mỗi vòng dựng và cache một tổ hợp chỉ báo), còn vòng lặp trong tỏa ra một lô rộng dọc trục rẻ trên những mảng đã cache đó. Trong code, hình dạng đúng y như vậy — một vòng lặp đắt nhỏ bọc quanh một lô rẻ rộng:

cache = SignalCache(base_close, base_ts)           # keyed by (period, hma_length)

for htf_p, htf_h, mtf_p, mtf_h, ltf_p, ltf_h in indicator_grid:   # EXPENSIVE axis (coarse)
    htf = cache.get(htf_p, htf_h)                  # built once, then a cache hit forever
    mtf = cache.get(mtf_p, mtf_h)
    ltf = cache.get(ltf_p, ltf_h)

    sps = sample_thresholds(m=4000)                # CHEAP axis: [m, 12] threshold vectors
    pnl, n_trades, bars_in_pos = sweep_separations(  # one compiled batch, no indicator work
        base_close, base_open, htf, ltf, sps, mtf=mtf)

Bạn chạm vào bộ dựng đắt đỏ ít lần nhất mà lưới chỉ báo cho phép, và để phép quét rẻ gánh khối lượng — hàng nghìn hàng sps chạy trên những mảng không bao giờ xê dịch. Đó là toàn bộ phép tối ưu — không xấp xỉ, không mất độ trung thực, chỉ đơn giản là từ chối tính lại thứ không hề thay đổi.

Cái giá thật của "miễn phí": những con số

Biểu đồ cột so sánh chi phí mỗi cấu hình: một cột cao cho phương án tính lại từng cấu hình bên cạnh một lát mỏng cho lượt duyệt ngưỡng đã cache, kèm chú thích khoảng cách xấp xỉ 1,600 lần

Chúng tôi đo hai trục trên khối lượng công việc demo: trọn một năm nến 1 phút ETHUSDT (~527k nến), ba khung thời gian, chỉ báo đã làm nóng và phần biên dịch JIT được loại khỏi phép đo thời gian.

Trên trục rẻ, sweep_separations duy trì ~5,600 cấu hình ngưỡng mỗi giây. Đó là một mô phỏng hoàn chỉnh — cổng, khớp lệnh, PnL, mức phơi nhiễm — cho mỗi cấu hình, trên hơn nửa triệu nến, ở mức xấp xỉ 180 micro giây mỗi cấu hình. Nó nhanh được đến vậy vì hoàn toàn không đụng đến việc tính chỉ báo: mọi cấu hình đều đọc cùng những mảng crossseparation đã cache.

Giờ hãy định giá phương án còn lại. Dựng bộ chỉ báo ba khung một lần mất cỡ vài trăm mili giây (~0.3 s) — tái lấy mẫu ba khung thời gian, lấy trọng số Hull, trích xuất giao cắt và độ tách trên cả năm dữ liệu. Nếu bạn tính lại chỉ báo bên trong vòng lặp cấu hình — thiết kế một vòng lặp ngây thơ — thì mỗi cấu hình trong số 5,600 cấu hình mỗi giây kia sẽ phải trả trọn chi phí dựng chỉ báo. Chi phí mỗi cấu hình phình từ ~180 micro giây lên ~0.3 giây:

Chi phí mỗi cấu hình Cấu hình/giây
Trục rẻ (tín hiệu đã cache) ~180 µs ~5,600
Tính lại chỉ báo cho từng cấu hình ~0.3 s ~3.4

Tỷ lệ là ~1,600 lần. Quét trục rẻ trên tín hiệu đã cache rẻ hơn xấp xỉ ba bậc độ lớn so với thiết kế ngây thơ dựng lại chỉ báo cho từng vector ngưỡng. Nói cụ thể: một lô vài nghìn cấu hình ngưỡng hoàn thành trong chưa đầy một giây trên đường đi có cache sẽ ngốn gần cả tiếng đồng hồ nếu mỗi cấu hình dựng lại chỉ báo của mình. Cùng kết quả, cùng độ trung thực, không đi tắt chỗ nào trong toán học — khác biệt duy nhất là một bên tính lại một đại lượng bất biến còn bên kia thì không.

Đây không phải kiểu vi tối ưu rắc thêm vào phút chót. Nó thay đổi những phép tìm kiếm nào là khả thi. Ở tốc độ 5,600 cfg/s, trục ngưỡng đủ dày để khám phá tử tế — bạn có thể cho phép mình một lưới mịn hoặc một mẫu ngẫu nhiên/QMC dài cho mỗi tổ hợp chỉ báo — trong khi trục đắt vẫn là một nhúm lần dựng thô một cách có chủ đích. Ngân sách tính toán chảy về nơi tham số thực sự có giá.

Lời nguyền số chiều, được định giá lại

Một lưới tham số phẳng đồng nhất được dán nhãn lại thành bản đồ giá hai tầng: vài ô đắt đỏ viền đậm, phần còn lại tô nhạt như gần miễn phí

Quay lại cách đặt vấn đề mở đầu bài viết. Lời nguyền số chiều nói rằng không gian tìm kiếm tăng theo hàm mũ với số tham số, nên nhiều chiều hơn là dứt khoát tệ hơn. Điều đó đúng về kích thước của lưới. Nhưng nó gây hiểu lầm về chi phí bao phủ lưới, vì nó định giá mọi chiều như nhau.

Một khi tách hai trục ra, con số đọc lên khác hẳn. Chiến lược ba khung có 18 chiều, nhưng chỉ 6 chiều trong số đó là đắt. 12 chiều còn lại là các chiều thuộc trục rẻ, mở rộng lưới mà không phình hóa đơn tính toán theo bất kỳ nghĩa đáng kể nào — bạn có thể ném hàng nghìn cấu hình vào chúng với giá vài xu. Chiến lược hai khung có 12 chiều với chỉ 4 chiều đắt và 8 chiều rẻ. Trong cả hai trường hợp, phần lớn "lời nguyền" tập trung vào cái trục gần như chẳng tốn gì để quét.

Vậy cách suy luận trung thực về chi phí tìm kiếm không phải là "bao nhiêu tham số" mà là "bao nhiêu tham số đắt, và lưới của chúng thô được đến đâu." Trục đắt là nơi hàm mũ thực sự gây đau, và là nơi bạn cần một lưới nhỏ, chọn lọc kỹ — vài chu kỳ ứng viên, một dải độ dài HMA vừa phải — có thể tinh chỉnh từ thô đến mịn, theo tinh thần của phép khoan sâu với độ phân giải thích ứng mà chúng tôi dùng ở nơi khác. Trục rẻ là nơi bạn được phép hào phóng, vì mỗi cấu hình ngưỡng thêm vào chỉ tốn 180 micro giây.

Phép định giá lại này tổng quát hóa vượt xa chiến lược HMA của chúng tôi. Khuôn mẫu — một số tham số thay đổi đặc trưng, phần lớn tham số thay đổi quy tắc áp lên các đặc trưng cố định — lặp lại khắp nơi trong giao dịch hệ thống. Độ dài chỉ báo, tần suất tái lấy mẫu và cửa sổ nhìn lại là đắt; ngưỡng vào/thoát lệnh, khoảng cách dừng lỗ, hệ số nhân khối lượng vị thế và các cổng xác nhận là rẻ. Bất cứ khi nào một tham số chỉ định hình lại ranh giới quyết định trên các tín hiệu đã tính sẵn, nó thuộc về trục rẻ, và nên được quét ở đó. Những chiến thắng nhờ cache cùng họ hàng xuất hiện trong bài về cache parquet đa khung thời gian và trong chiếc thang tốc độ engine rộng hơn.

Nơi bữa trưa miễn phí vẫn có hóa đơn

Trục rẻ chỉ rẻ về tính toán. Nó không hề rẻ về thống kê, và đánh đồng hai thứ này chính là cách bạn biến một chiến thắng hiệu năng thành cỗ máy quá khớp (overfitting).

Mỗi cấu hình ngưỡng bạn đánh giá là một phép thử, và khi chạy hàng nghìn phép thử trên một bộ dữ liệu, phép thử tốt nhất trông đẹp một phần là nhờ may mắn. Làm cho các phép thử rẻ đi 1,600 lần không làm may mắn biến mất — nó chỉ làm may mắn dễ tích tụ hơn. Một phép quét ngưỡng dày đặc chính xác là tình huống mà lạm phát do kiểm định nhiều lần cắn đau nhất: nhiều ứng viên, một lịch sử, và một quy tắc chọn lọc chỉ báo cáo giá trị cực đại. Engine nhanh nhất thế giới sẽ vui vẻ trao cho bạn một vector ngưỡng khớp tuyệt đẹp với nhiễu của cửa sổ kiểm thử rồi thất bại ngoài mẫu.

Vì vậy kỷ luật phải tăng theo tốc độ. Khoảnh khắc chi phí tính toán của một phép thử tiến về không, sổ sách kế toán thống kê trở thành ràng buộc quyết định, và bạn phải trả nó một cách tường minh:

  • Khấu trừ theo số phép thử. Chấm điểm kẻ chiến thắng dựa trên số cấu hình bạn đã thử, chứ không phải so với con số không. Tỷ lệ Sharpe hiệu chỉnh (Deflated Sharpe Ratio)xác suất quá khớp của backtest tồn tại đúng cho việc này — chúng biến "chúng tôi đã thử 4,000 vector ngưỡng" thành một khoản chiết khấu trên lợi thế được báo cáo.
  • Kiểm chứng ngoài mẫu, theo từng fold. Một phép quét rẻ vẫn phải chạy bên trong một phép chia walk-forward trung thực; một ngưỡng chỉ thắng trong mẫu thì vô giá trị dù bạn tìm ra nó nhanh đến đâu. Engine của chúng tôi giữ khớp lệnh không rò rỉ (open[i+1]) chính là để trục rẻ không thể mua hiệu suất bằng cách nhìn trộm tương lai.
  • Ưu tiên vùng bằng phẳng hơn đỉnh nhọn. Vì trục ngưỡng vừa dày vừa nhanh, bạn có thể vẽ bản đồ cả bề mặt đáp ứng của nó, chứ không chỉ điểm argmax. Một vùng ngưỡng rộng mà tất cả đều hoạt động là lợi thế thật; một cực đại nhọn hoắt đơn độc là sản phẩm của việc khớp nhiễu — phép phân biệt vùng bằng phẳng và đỉnh nhọn, trở nên vừa túi tiền nhờ chính tốc độ mà chúng ta đang mô tả.

Hãy đọc cấu trúc hai trục theo đúng cách: nó không mua cho bạn nhiều niềm tin hơn, nó mua cho bạn nhiều phép thử hơn với cùng cái giá niềm tin trên mỗi phép thử. Điều đó thực sự đáng giá — độ phủ dày đặc trên trục rẻ là thứ cho phép bạn tìm ra các vùng bằng phẳng và mô tả đặc điểm của bề mặt — nhưng chỉ khi bạn giữ sổ sách thống kê trung thực. Tốc độ xóa đi cái cớ tính toán cho việc không tìm kiếm kỹ lưỡng; nó không xóa đi nghĩa vụ chiết khấu những gì việc tìm kiếm kỹ lưỡng tìm ra.

Cách tổ chức phép tìm kiếm của riêng bạn

Để áp dụng điều này cho chiến lược của riêng bạn, công việc chủ yếu là phân loại — xếp các tham số vào đúng trục — rồi đến một vòng lặp lồng nhau:

  1. Dán nhãn mỗi tham số theo thứ nó buộc bạn tính lại. Nếu thay đổi nó làm thay đổi một mảng chỉ báo/đặc trưng, nó là đắt. Nếu nó chỉ thay đổi một phép so sánh, một ngưỡng, hay một quy tắc định cỡ vị thế áp lên các mảng cố định, nó là rẻ. Khi phân vân, hãy hỏi: tham số này có xuất hiện ở đâu đó bên trong định nghĩa của chỉ báo không? Nếu không, nó rẻ.
  2. Khóa cache chỉ theo trục đắt. Memo hóa việc xây dựng đặc trưng theo các tham số chỉ báo của nó (như SignalCache làm theo (period, hma_length)). Các phép thử liền kề dùng chung một tổ hợp chỉ báo khi đó tái sử dụng cùng những mảng ấy hoàn toàn miễn phí.
  3. Chạy trục rẻ theo lô trên các đặc trưng đã cache. Chạy các cấu hình ngưỡng như một vòng lặp biên dịch gọn chặt trên các tín hiệu đã tính trước, chứ không phải như những phép đánh giá lại đầy đủ. Đây là nguồn gốc thông lượng của bạn — và trong trường hợp của chúng tôi là con số ~5,600 cfg/s.
  4. Lồng các vòng lặp: đắt ở ngoài, rẻ ở trong. Giữ lưới đắt nhỏ và có chủ đích (thô, hoặc từ thô đến mịn); để phép quét rẻ được dày đặc. Tiêu ngân sách vào nơi tham số thực sự có giá.
  5. Lập ngân sách phép thử theo mức quá khớp, không phải theo đồng hồ. Giờ đây khi đồng hồ không còn là giới hạn, hãy để Sharpe hiệu chỉnh / PBO làm giới hạn. Quyết định xem bạn có thể chi trả bao nhiêu phép thử trên trục rẻ về mặt thống kê, và kiểm chứng kẻ chiến thắng ngoài mẫu.

Phần thưởng kỹ thuật và hàng rào bảo vệ thống kê là hai mặt của cùng một ý tưởng: tách các trục cho phép bạn tìm kiếm cạn kiệt các chiều rẻ, và đó chính xác là lý do sau đó bạn phải chiết khấu mức độ cạn kiệt của phép tìm kiếm ấy.

Điểm chính rút ra

  1. Không phải chiều nào cũng có giá như nhau. Tham số của một chiến lược tách thành trục đắt (chỉ báo — tính lại trên toàn chuỗi) và trục rẻ (ngưỡng — một lượt duyệt O(n) trên tín hiệu đã tính sẵn). Trong engine của chúng tôi, đó là 6 đắt + 12 rẻ cho ba khung, 4 + 8 cho hai khung.
  2. Chỉ báo bất biến với ngưỡng, và chính sự bất biến đó là toàn bộ phép tối ưu. Việc xây dựng đặc trưng chỉ phụ thuộc vào tham số chỉ báo, nên bạn dựng một lần, cache theo (period, hma_length), và tái sử dụng trên mọi cấu hình ngưỡng dùng chung tổ hợp đó.
  3. Trục rẻ chạy rẻ hơn ~1,600 lần. ~5,600 cấu hình ngưỡng/giây (~180 µs mỗi cấu hình) trên tín hiệu đã cache, so với ~0.3 s mỗi cấu hình nếu bạn dựng lại chỉ báo mỗi lần. Cùng độ trung thực — khác biệt duy nhất là từ chối tính lại một đại lượng bất biến.
  4. Lời nguyền số chiều thực chất là lời nguyền của số chiều đắt. Phần lớn số tham số nằm trên cái trục gần như miễn phí khi quét. Giữ lưới đắt ở mức thô; hào phóng với lưới rẻ.
  5. Tốc độ dời ràng buộc quyết định từ tính toán sang thống kê. Một phép quét dày và nhanh là một cỗ máy kiểm định nhiều lần. Hãy khấu trừ theo số phép thử, kiểm chứng theo từng fold, và ưu tiên vùng bằng phẳng hơn đỉnh nhọn — bữa trưa miễn phí là có thật, nhưng hóa đơn thống kê không phải khoản tùy chọn.

Toàn bộ engine — SignalCache, sweep_separations, phần mô phỏng đa khung không rò rỉ, và bài kiểm thử đối chiếu ghim nó vào đúng ngữ nghĩa nến-đang-hình-thành như khi chạy thật — nằm trong scripts/engine_multitf.py (commit bfc8aaa) trong backtester của chúng tôi. Lần tới khi ai đó bảo bạn rằng một chiến lược 18 tham số là quá lớn để tìm kiếm, hãy hỏi họ xem bao nhiêu trong số các tham số đó thực sự làm chỉ báo dịch chuyển. Thường thì chỉ một phần ba, và phần còn lại gần như miễn phí.

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.