Circuit Breaker cho trading bot là gì?
Circuit breaker ngăn trading bot giao dịch tiếp khi chạm giới hạn lỗ ngày. Tìm hiểu tần suất kích hoạt và cách volatility scaling điều chỉnh quy mô vị thế.
Circuit breaker cho trading bot là các quy tắc ngăn chiến lược tự động gửi lệnh khi một giới hạn đã đặt được chạm tới. Các quy tắc này nằm trong lớp quản trị rủi ro giữa chiến lược và broker, đồng thời áp dụng cho mọi lệnh, bất kể chiến lược có đồng ý hay không. Chiến lược quyết định giao dịch tài sản nào. Lớp quản trị rủi ro quyết định có cho phép giao dịch diễn ra hay không.
Sự tách biệt này là nền tảng của toàn bộ thiết kế. Một chiến lược tự giám sát sẽ không có cơ chế kiểm tra độc lập tại thời điểm các giả định của chính nó bắt đầu sai lệch. Đó lại chính là lúc cơ chế kiểm tra có giá trị nhất.
Bộ ngắt mạch làm gì trong một bot giao dịch
Một lớp quản trị rủi ro có bốn phần. Mỗi phần nhằm ngăn một lỗi cụ thể và thường gặp.
- Các giới hạn cứng đối với quy mô vị thế, mức độ phơi nhiễm danh nghĩa theo từng mã và tần suất đặt lệnh. Các giới hạn này khống chế thiệt hại từ một lỗi phần mềm mà nếu không có chúng, thiệt hại có thể tăng không giới hạn.
- Bộ ngắt mạch drawdown dừng các lệnh mới khi tài khoản giảm một mức xác định trong phiên hoặc giảm một mức xác định so với đỉnh vốn chủ sở hữu.
- Quy mô lệnh được điều chỉnh, dựa trên biến động gần đây hoặc một phần của mức cược Kelly thay vì số lượng cổ phiếu cố định. Cách này giúp giữ rủi ro trên mỗi giao dịch tương đối ổn định khi biên độ dao động của thị trường thay đổi.
- Nhật ký kiểm toán chỉ ghi nối tiếp, lưu mọi quyết định, bao gồm cả những lệnh bị lớp quản trị rủi ro từ chối. Đây là bằng chứng duy nhất giúp phân biệt giữa “chiến lược đã sai” và “bước kiểm tra chưa từng được thực hiện”.
Toàn bộ phần dưới đây cụ thể hóa một trong bốn nội dung đó.
Mức lỗ hằng ngày hợp lý cho một trading bot là bao nhiêu?
Giới hạn lỗ hằng ngày sẽ dừng đặt lệnh mới khi mức lỗ trong phiên vượt qua một ngưỡng nhất định. Việc chọn con số này là vấn đề hiệu chỉnh, không phải sở thích: đặt ngưỡng trong phạm vi nhiễu thị trường thông thường thì bot sẽ bị dừng trong hầu hết các tuần; đặt quá xa thì ngưỡng gần như không bao giờ được kích hoạt. Điểm khởi đầu là tần suất thị trường tự ghi nhận một phiên giảm với quy mô nhất định.
Câu lệnh SQL chính xác đằng sau mỗi con số
WITH daily AS (
SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS d,
argMax(toFloat64(close), window_start) AS close_px
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2017-12-01')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-07-31')
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
GROUP BY d
),
with_prev AS (
SELECT d,
close_px,
any(close_px) OVER (ORDER BY d ASC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS prev_close
FROM daily
)
SELECT toYear(d) AS year,
countIf(close_px / prev_close - 1 <= -0.01) AS down_1pct_days,
countIf(close_px / prev_close - 1 <= -0.02) AS down_2pct_days,
countIf(close_px / prev_close - 1 <= -0.03) AS down_3pct_days
FROM with_prev
WHERE prev_close > 0
AND d >= toDate('2018-01-01')
GROUP BY year
ORDER BY yearCác phiên giảm từ 1% trở lên có tổng cộng 15 phiên trong 2019 và 45 phiên trong 2020, trên tổng số khoảng 250 ngày giao dịch mỗi năm. Số phiên giảm từ 3% trở lên lại là câu chuyện khác: 0 phiên trong 2019 so với 16 phiên trong 2020. Dòng cuối của 9 chỉ bao gồm các phiên đến ngày 31 tháng 7 năm 2026.
Đường dữ liệu này biến động theo từng cụm thay vì ổn định, và đó chính là điểm cần lưu ý khi thiết kế giới hạn. Các phiên khó khăn thường xuất hiện theo cụm. Một bot dừng trong ngày đầu của một cụm rồi hoạt động trở lại vào ngày thứ hai thì thực tế chưa dừng đủ lâu.
Hai ngưỡng này có hai chức năng khác nhau. Giới hạn lỗ hằng ngày, thường là 2% vốn chủ sở hữu trong tài khoản, sẽ kết thúc phiên giao dịch. Giới hạn drawdown trượt, được tính từ mức đỉnh vốn chủ sở hữu, thường vào khoảng 10%, sẽ dừng chiến lược để chờ con người xem xét. Ngưỡng thứ nhất nhằm xử lý tình huống thông thường, còn ngưỡng thứ hai được thiết kế để hiếm khi xảy ra. Một bot chỉ có ngưỡng thứ nhất vẫn có thể làm tài khoản giảm dần 2% mỗi lần mà không bao giờ kích hoạt cơ chế dừng.
Quy mô vị thế thay đổi như thế nào khi điều chỉnh theo biến động?
Chiến lược nhắm mục tiêu biến động xác định quy mô vị thế theo hướng ngược với biến động thực tế gần đây: khi biên độ dao động hằng ngày tăng gấp đôi, quy mô vị thế giảm khoảng một nửa, qua đó giữ rủi ro tính theo USD trên mỗi giao dịch gần như ổn định. Biến động thực tế ở đây là độ lệch chuẩn thường niên hóa của lợi suất hằng ngày. Chỉ số này biến động mạnh hơn nhiều so với kỳ vọng của phần lớn nhà đầu tư.
Câu lệnh SQL chính xác đằng sau mỗi con số
WITH daily AS (
SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS d,
argMax(toFloat64(close), window_start) AS close_px
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2023-12-01')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-07-31')
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
GROUP BY d
),
rets AS (
SELECT d,
close_px / any(close_px) OVER (ORDER BY d ASC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) - 1 AS ret
FROM daily
)
SELECT formatDateTime(toStartOfMonth(d), '%Y-%m') AS month,
round(stddevSamp(ret) * sqrt(252) * 100, 1) AS realized_vol_pct,
round(least(100.0, 1200.0 / (stddevSamp(ret) * sqrt(252) * 100)), 1) AS vol_target_size_pct
FROM rets
WHERE d >= toDate('2024-01-01')
AND isFinite(ret)
GROUP BY toStartOfMonth(d)
HAVING count() >= 15
ORDER BY toStartOfMonth(d)Biến động thực tế được đo ở mức 11.1% thường niên hóa trong 2024-01 và 12% trong 2026-07, trong suốt 31 tháng. Cột thứ hai chuyển từng mức biến động thành quy mô vị thế mà mục tiêu biến động 12% sẽ duy trì, với giới hạn tối đa là toàn bộ dòng: 100% trong 2024-01 so với 99.7% trong 2026-07. Cùng một chiến lược, cùng mức độ tin tưởng, nhưng số lượng cổ phiếu nắm giữ khác biệt đáng kể.
Tiêu chí Kelly tiếp cận vấn đề này từ hướng ngược lại. Phương pháp này xác định quy mô vị thế dựa trên lợi thế ước tính và phương sai, thay vì chỉ dựa vào biến động. Phần lớn các nhà vận hành hệ thống chỉ sử dụng một phần của mức Kelly, thường là một nửa hoặc một phần tư Kelly, vì cả hai đầu vào đều là các ước tính dựa trên một mẫu dữ liệu hữu hạn. Xác định quy mô vị thế theo tiêu chí Kelly trình bày phép tính này.
Tại sao trading bot liên tục vào lại sau khi bị stop?
Lệnh stop được kích hoạt. Vị thế đóng. Chín mươi giây sau, điều kiện vào lệnh lại đúng, bot vào lại, chịu cùng một khoản lỗ rồi lặp lại. Không có thành phần riêng lẻ nào bị hỏng. Strategy thực hiện đúng như được lập trình, lệnh stop cũng vậy, nhưng tài khoản vẫn cạn dần qua từng vòng giao dịch.
Tần suất này xuất phát trực tiếp từ đường đi của giá. Bảng này đếm theo từng phiên số lần SPY di chuyển từ mức cao hơn giá mở cửa 0,1% xuống mức thấp hơn giá mở cửa 0,1%, hoặc di chuyển theo chiều ngược lại.
Câu lệnh SQL chính xác đằng sau mỗi con số
WITH mins AS (
SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS d,
window_start AS ts,
toFloat64(close) AS px
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2025-08-01')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-07-31')
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
),
opens AS (
SELECT d, argMin(px, ts) AS open_px
FROM mins
GROUP BY d
),
zoned AS (
SELECT m.d AS d,
m.ts AS ts,
multiIf(m.px >= o.open_px * 1.001, 1,
m.px <= o.open_px * 0.999, -1,
0) AS zone
FROM mins AS m
INNER JOIN opens AS o ON m.d = o.d
),
flips AS (
SELECT d,
zone,
any(zone) OVER (PARTITION BY d ORDER BY ts ASC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS prev_zone
FROM zoned
WHERE zone != 0
),
per_day AS (
SELECT d, countIf(prev_zone != 0 AND zone != prev_zone) AS crossings
FROM flips
GROUP BY d
)
SELECT formatDateTime(toStartOfMonth(d), '%Y-%m') AS month,
round(avg(crossings), 1) AS avg_crossings_per_session,
max(crossings) AS max_crossings_in_a_session
FROM per_day
GROUP BY toStartOfMonth(d)
ORDER BY toStartOfMonth(d)SPY cắt qua biên độ đó trung bình 0.7 lần mỗi phiên trong 2025-08 và 1.5 lần trong 2026-07. Trong đó, có một phiên vào 2026-07 ghi nhận 5 lần cắt qua. Bất kỳ quy tắc nào mở vị thế ở một phía của một mức giá và đặt stop ở phía còn lại đều có số cơ hội tương ứng để kích hoạt trong một ngày.
Bốn cơ chế giúp kiểm soát tình trạng này.
- Cooldown sau mỗi lần stop, tính theo phút hoặc số bar. Trong khoảng thời gian đó, không lệnh mới nào của mã đó được vượt qua lớp kiểm soát rủi ro.
- Giới hạn số giao dịch hằng ngày theo từng mã. Cơ chế này biến một vòng lặp không giới hạn thành một vòng lặp có giới hạn.
- Cờ tạm dừng có trạng thái được duy trì. Khi giới hạn lỗ trong ngày bị kích hoạt, cờ vẫn ở trạng thái đó cho đến khi có người xóa.
- Lưu trạng thái của cờ bên ngoài bộ nhớ của process. Một supervisor khởi động lại bot bị treo sẽ cung cấp cho bot một trạng thái ban đầu sạch. Chính trạng thái này là điều cờ được thiết kế để ngăn chặn.
Cơ chế cuối cùng thường bị bỏ sót dù các bước khác đã được thực hiện đúng. Bot giao dịch Grid vốn được thiết kế để đặt các bậc lệnh, nên giới hạn số giao dịch là một cơ chế thiết yếu chứ không phải hình thức.
Điều gì xảy ra khi bot giao dịch dựa trên nguồn giá đã lỗi thời?
Một báo giá đã ngừng cập nhật vẫn hiển thị như một con số. Bot đọc con số đó, định giá lệnh dựa trên nó rồi gửi lệnh vào một thị trường đã dịch chuyển. Lỗi này diễn ra âm thầm: không có ngoại lệ, không có nhật ký báo lỗi, và chỉ sau đó các giao dịch khớp mới cho thấy dấu hiệu bất thường.
Cách đo lường rõ ràng nhất là khoảng gap qua đêm. Trong trường hợp này, một mức giá đã biết đứng yên trong nhiều giờ, trong khi giá có thể giao dịch tiếp tục dịch chuyển.
Câu lệnh SQL chính xác đằng sau mỗi con số
WITH daily AS (
SELECT ticker,
toDate(toTimeZone(window_start, 'America/New_York')) AS d,
argMin(toFloat64(open), window_start) AS open_px,
argMax(toFloat64(close), window_start) AS close_px
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('SPY', 'KO', 'MSFT', 'AAPL', 'NVDA', 'TSLA')
AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2024-01-01')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-07-31')
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
GROUP BY ticker, d
),
gaps AS (
SELECT ticker,
d,
open_px,
any(close_px) OVER (PARTITION BY ticker ORDER BY d ASC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS prev_close
FROM daily
)
SELECT ticker,
round(quantileDeterministic(0.5)(abs(open_px / prev_close - 1) * 100, cityHash64(ticker, d)), 2) AS median_gap_pct,
round(quantileDeterministic(0.95)(abs(open_px / prev_close - 1) * 100, cityHash64(ticker, d)), 2) AS p95_gap_pct,
round(max(abs(open_px / prev_close - 1) * 100), 2) AS max_gap_pct
FROM gaps
WHERE prev_close > 0
GROUP BY ticker
ORDER BY p95_gap_pct DESCMã có khoảng gap ở phân vị 95 lớn nhất là TSLA, ở mức 4.41%, so với 1% của KO. Phần lớn các đêm yên ắng hơn nhiều: khoảng gap trung vị lần lượt là 1.01% và 0.24%. Lớp kiểm soát rủi ro được xây dựng để xử lý các đuôi phân phối, và khoảng gap đơn lẻ lớn nhất trên TSLA trong giai đoạn đo lường là 14.57%. Đây là những khoảng cách mà bot có thể phải đối mặt khi hành động dựa trên mức giá đã đọc từ trước. Vì sao cổ phiếu tạo gap qua đêm giải thích cơ chế này.
Các biện pháp phòng vệ không tốn kém. Hãy đặt thời hạn tối đa cho mọi báo giá mà lớp kiểm soát rủi ro dùng để định giá lệnh, thường là vài giây đối với chiến lược giao dịch trong ngày. Hãy lấy tín hiệu heartbeat riêng từ nguồn dữ liệu, thay vì suy ra từ dữ liệu, để phân biệt socket im lặng với một thị trường đang ít biến động. Hãy xử lý dữ liệu bị thiếu như trạng thái tạm dừng giao dịch thay vì giữ nguyên mức giá, vì bot không có giá cũng không thể đánh giá các lệnh thoát vị thế.
Các giới hạn cứng nào thuộc lớp quản trị rủi ro?
- Giá trị danh nghĩa tối đa trên mỗi mã, tính theo tỷ trọng trong vốn chủ sở hữu của tài khoản. Mức trần 10% giúp một mã diễn biến xấu không làm ảnh hưởng đến toàn bộ tài khoản.
- Tổng giá trị danh nghĩa gộp tối đa của tất cả vị thế đang mở. Đặt mức này ở 100% vốn chủ sở hữu nghĩa là không sử dụng đòn bẩy. Đây là quyết định cần được xác định rõ thay vì mặc nhiên theo cài đặt mặc định của broker.
- Tần suất đặt lệnh tối đa theo phút và theo ngày. Mười lệnh mỗi phút là mức khá rộng đối với hầu hết chiến lược của nhà đầu tư cá nhân, nhưng vẫn đủ để giới hạn một vòng lặp mất kiểm soát trong khoảng thời gian một phút.
- Quy mô lệnh tối đa theo tỷ trọng trong khối lượng giao dịch bình quân ngày của mã. Mức trần 1% giúp bot không đẩy giá theo hướng bất lợi khi giao dịch, trong đó khối lượng giao dịch bình quân ngày là mẫu số.
Mọi giới hạn trên đều thuộc lớp quản trị rủi ro, không phải lớp chiến lược. Tất cả phải chạy qua cùng một luồng mã trong backtest, paper trading và giao dịch live. Một giới hạn chỉ tồn tại trong môi trường live là giới hạn chưa từng được kiểm thử.
Nhật ký kiểm toán của trading bot cần có gì?
Một nhật ký chỉ ghi nối tiếp (append-only) tạo một bản ghi cho mỗi quyết định và không bao giờ chỉnh sửa hoặc xóa bản ghi. Mỗi bản ghi phải có timestamp, báo giá được sử dụng và độ cũ của báo giá, mọi lần kiểm tra giới hạn đã chạy cùng kết quả, lệnh đã gửi và phản hồi của broker. Các lệnh bị từ chối phải được ghi nhận với mức độ đầy đủ như các lệnh được khớp.
Mục tiêu là có thể tái dựng. Sáu tuần sau một phiên giao dịch tồi tệ, câu hỏi không phải là P&L khi đó là bao nhiêu. Câu hỏi là lần kiểm tra nào đã đạt, dựa trên dữ liệu đầu vào nào. Nếu không lưu dữ liệu đầu vào, bạn sẽ phải suy ra lại trạng thái của bot từ trạng thái thị trường. Đây chính là sai lầm giống như thiên lệch nhìn trước trong backtesting: sử dụng thông tin mà hệ thống chưa có tại thời điểm ra quyết định.
Dự án riskguard là một triển khai mã nguồn mở cho cách tách biệt này. Các lần kiểm tra giới hạn nằm trong một component mà strategy gọi tới, thay vì bị rải rác trong chính strategy. Đây chỉ là một trong nhiều thiết kế, nên đọc để tham khảo thay vì áp dụng ngay. Dù phụ thuộc vào công cụ nào, bạn nên cố định một bản phát hành đã gắn thẻ thay vì dùng default branch. Một branch có thể thay đổi giữa hai lần chạy cùng một backtest. Một lớp quản trị rủi ro âm thầm thay đổi còn tệ hơn việc không có lớp này.
Vì sao lần triển khai đầu tiên nên chạy trên tài khoản paper
Bộ kết nối broker mặc định sử dụng paper, còn giao dịch live phải được kích hoạt bằng một cờ rõ ràng. Cơ chế này nhằm ngăn một lỗi rất phổ biến: file cấu hình bị sao chép hoặc biến môi trường chưa được ghi đè khiến hệ thống gửi lệnh thật bằng tiền thật.
Một lần chạy trên paper cũng tạo ra tài liệu quan trọng: nhật ký quyết định từ cùng lớp quản trị rủi ro, dựa trên giá thị trường live, cho thấy giới hạn nào đã được kích hoạt và giới hạn nào chưa. Đây là bằng chứng về lớp quản trị rủi ro, khác với câu hỏi liệu chiến lược có sinh lời hay không. Giao dịch paper trước khi dùng tiền thật trình bày những gì một bản ghi paper có thể và không thể chứng minh. các hệ thống giao dịch AI đa tác nhân giải thích vì sao quyền dừng giao dịch phải nằm ngoài mọi agent khi nhiều agent có thể đặt lệnh.
Câu hỏi thường gặp về bộ ngắt mạch của trading bot
Bộ ngắt mạch trong trading bot là gì?
Đây là một quy tắc trong lớp quản trị rủi ro. Quy tắc này dừng bot gửi lệnh mới khi chạm đến một giới hạn đã đặt, thường là ngưỡng lỗ trong ngày hoặc mức drawdown tính từ đỉnh equity của tài khoản. Quy tắc hoạt động độc lập với chiến lược và được kiểm tra trên từng lệnh. Nó vẫn ở trạng thái kích hoạt cho đến khi được giải phóng.
Thị trường thường có bao nhiêu phiên giảm từ 2% trở lên?
SPY có 5 phiên giảm từ 2% trở lên trong 2019 và 25 trong 2020, trên tổng số khoảng 250 ngày giao dịch mỗi năm. Chênh lệch giữa một năm bình lặng và một năm căng thẳng là lý do giới hạn lỗ cần được hiệu chỉnh dựa trên dữ liệu lịch sử, thay vì trực giác.
Làm thế nào để ngăn bot vào lại lệnh sau khi bị dừng?
Đặt thời gian cooldown sau mỗi lần dừng và giới hạn số giao dịch mỗi ngày cho từng mã sẽ biến một vòng lặp không giới hạn thành một quy trình có giới hạn. Cờ halt cũng phải được ghim và lưu bên ngoài bộ nhớ của tiến trình. Nếu không, supervisor khởi động lại bot bị lỗi sẽ cấp cho bot một trạng thái mới, chưa bị dừng.
Làm thế nào để bot nhận biết nguồn cung cấp giá đã bị stale?
Bot kiểm tra độ cũ của từng báo giá trước khi dùng giá đó để định giá lệnh. Bot cũng theo dõi riêng heartbeat của nguồn dữ liệu, thay vì chỉ dựa vào dữ liệu giá. Khoảng trống giá qua đêm cho thấy mức độ mà một mức giá stale có thể che giấu: biến động qua đêm ở phân vị 95 đạt 4.41% vào TSLA trong giai đoạn từ tháng 1 năm 2024 đến tháng 7 năm 2026.
Một trading bot nhỏ có thực sự cần audit log không?
Log về các lệnh khớp ghi lại những gì đã xảy ra. Log về quyết định ghi lại những gì bot cho rằng mình được phép thực hiện. Đây là cách duy nhất để phân biệt một chiến lược sai với trường hợp kiểm tra rủi ro chưa từng chạy. Phiên bản tối thiểu hữu ích phải là log chỉ được ghi nối tiếp, bao gồm cả các lệnh bị từ chối.
Mọi số liệu trên đều đến từ một stored query chạy trên dữ liệu nến phút. Bạn có thể mở từng bảng để xem câu lệnh SQL phía sau. Hãy chạy các query tương tự trên danh sách mã của bạn tại terminal Strasmore.