Maximum Drawdown là gì? Độ sâu và thời gian phục hồi
Maximum drawdown là mức giảm lớn nhất từ đỉnh xuống đáy trong một chuỗi giá trị. Tìm hiểu thế nào là mức giảm sâu và vì sao phục hồi về đỉnh mới thường lâu hơn.
Mức sụt giảm tối đa (maximum drawdown) là mức giảm lớn nhất từ đỉnh xuống đáy trong một chuỗi giá trị, tính theo tỷ lệ phần trăm so với đỉnh lũy kế. Một tài khoản tăng lên 120.000 USD, giảm xuống 84.000 USD rồi phục hồi sẽ có mức sụt giảm tối đa là 30%. Con số này được giữ nguyên về sau vì chỉ số ghi nhận giai đoạn tồi tệ nhất trong lịch sử, không phản ánh trạng thái hiện tại. Phần lớn mọi người chỉ đề cập đến độ sâu của mức sụt giảm rồi dừng lại. Tuy nhiên, cùng một chỉ số còn bao gồm hai khoảng thời gian khác: thời gian từ đỉnh xuống đáy và khoảng thời gian dài hơn nhiều từ đáy trở lại mức đỉnh mới.
Tính mức sụt giảm tối đa như thế nào?
Toàn bộ phép tính chỉ cần duyệt qua chuỗi dữ liệu một lần. Ở mỗi quan sát, bạn cập nhật mức đỉnh đang theo dõi, so sánh giá trị hiện tại với mức đỉnh đó và lưu lại mức giảm tệ nhất đã ghi nhận.
- Đặt đỉnh đang theo dõi bằng giá trị đầu tiên.
- Với mỗi giá trị mới, nâng mức đỉnh nếu giá trị đó cao hơn.
- Tính mức sụt giảm tại thời điểm đó: lấy giá trị chia cho đỉnh, rồi trừ một.
- Lưu lại mức thấp nhất. Đó là mức sụt giảm tối đa.
Phép tính này không sử dụng chuỗi lợi suất, ước tính biến động hay giả định về phân phối. Vòng lặp chạy trên một danh sách số đơn giản bằng thư viện chuẩn của Python. Trên một máy Ubuntu mới cài đặt, trước tiên hãy cài trình thông dịch:
apt-get update && apt-get install -y python3
Lưu vòng lặp dưới tên maxdd.py. Chuỗi giá dưới đây gồm ten điểm dữ liệu giả lập và đủ ngắn để kiểm tra thủ công:
prices = [100.0, 104.0, 98.0, 92.0, 95.0, 88.0, 90.0, 101.0, 99.0, 106.0]
peak = prices[0]
max_dd = 0.0
for price in prices:
if price > peak:
peak = price
drawdown = price / peak - 1.0
if drawdown < max_dd:
max_dd = drawdown
print(f"maximum drawdown: {max_dd * 100:.1f}%")
Chạy python3 maxdd.py và kết quả là maximum drawdown: -15.4%. Hãy kiểm tra thủ công: mức đỉnh trước khi chạm đáy là 104, mức đáy là 88, và 88 chia cho 104 bằng 0,846. Sau đó, chuỗi kết thúc ở 106, là mức cao nhất mọi thời đại, nhưng mức sụt giảm 15,4% vẫn được giữ nguyên. Mức sụt giảm tối đa là kỷ lục cố định về thời điểm tệ nhất trong mẫu dữ liệu và không cho biết chuỗi đang ở vị trí nào hiện nay. Có thể bổ sung hai biến trong cùng vòng lặp để xác định thời điểm: chỉ số tại đó mức đỉnh được thiết lập và chỉ số tại đó mức sụt giảm tệ nhất xuất hiện.
Đường cong drawdown trông như thế nào
Đường cong drawdown duy trì bước 3 tại từng thời điểm, thay vì rút gọn thành một giá trị tệ nhất duy nhất. Đường này nằm ở mức 0 mỗi khi chuỗi dữ liệu ghi nhận một mức đỉnh mới và nằm dưới 0 trong khoảng thời gian giữa các đỉnh. Vì vậy, khoảng cách này được gọi là “underwater”. Dưới đây là đường cong của SPY, một quỹ ETF mô phỏng chỉ số S&P 500, dựa trên giá đóng cửa cuối tháng kể từ tháng 1 năm 2016.
Câu lệnh SQL chính xác đằng sau mỗi con số
WITH monthly AS (
SELECT toStartOfMonth(toDate(toTimeZone(window_start, 'America/New_York'))) AS month_start,
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('2016-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 month_start
),
runs AS (
SELECT month_start,
close_px,
max(close_px) OVER (ORDER BY month_start
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_peak
FROM monthly
)
SELECT formatDateTime(month_start, '%Y-%m') AS month,
round(100 * (close_px / running_peak - 1), 2) AS drawdown_pct
FROM runs
ORDER BY month_startTrong 127 lần ghi nhận cuối tháng, đường cong nằm gần mức 0 trong những khoảng thời gian dài rồi giảm xuống một vài vùng lõm sâu. Tháng gần nhất trong dữ liệu hiện ở mức -1.27% so với đỉnh lũy kế. Hình dạng đường cong cho thấy hai đặc điểm quan trọng. Quá trình phục hồi diễn ra bằng những nhịp tăng chậm, thay vì các cú bật mạnh. Mỗi đỉnh mới đưa đường cong trở về 0, xóa đi dấu vết trực quan của những gì đã xảy ra trước đó.
Mất bao lâu để phục hồi sau một đợt sụt giảm?
Một đợt sụt giảm bao gồm ba con số riêng biệt. Việc xem chúng như một con số là cách diễn giải sai phổ biến nhất khi đọc bảng hiệu suất:
- Mức giảm: giá trị đã giảm bao xa so với đỉnh trước đó, tính theo phần trăm.
- Thời gian xuống đáy: số ngày theo lịch từ đỉnh đến đáy.
- Thời gian phục hồi: số ngày theo lịch từ đáy trở lại mức đỉnh mới.
Thông thường, thời gian phục hồi là dài nhất. Đây cũng là con số mà một chỉ báo tổng hợp không bao giờ thể hiện. Bảng dưới đây tái dựng mọi giai đoạn giảm đã hoàn tất của SPY kể từ năm 2016, dựa trên giá đóng cửa hằng ngày. Một giai đoạn bắt đầu vào ngày quỹ thiết lập đỉnh và kết thúc vào ngày quỹ lần đầu đóng cửa cao hơn mức đỉ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 session_date,
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('2016-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 session_date
),
runs AS (
SELECT session_date,
close_px,
max(close_px) OVER (ORDER BY session_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_peak
FROM daily
),
episodes AS (
SELECT running_peak AS peak_px,
min(session_date) AS peak_date,
argMin(session_date, close_px) AS trough_date,
min(close_px) AS trough_px
FROM runs
GROUP BY running_peak
),
sequenced AS (
SELECT peak_px,
peak_date,
trough_date,
trough_px,
leadInFrame(peak_date, 1) OVER (ORDER BY peak_date
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS recovery_date
FROM episodes
)
SELECT formatDateTime(peak_date, '%b %Y') AS episode_label,
round(100 * (1 - trough_px / peak_px), 1) AS fall_from_peak_pct,
dateDiff('day', peak_date, trough_date) AS days_to_trough,
dateDiff('day', trough_date, recovery_date) AS days_to_new_high,
dateDiff('day', peak_date, recovery_date) AS days_underwater
FROM sequenced
WHERE recovery_date > trough_date
AND round(100 * (1 - trough_px / peak_px), 1) >= 5
ORDER BY fall_from_peak_pct DESC
LIMIT 8Giai đoạn giảm sâu nhất trong danh sách bắt đầu vào Feb 2020 và chạm mức thấp hơn 34.2% so với đỉnh trước đó. Giai đoạn này mất 33 ngày theo lịch để giảm và 148 ngày để phục hồi, với tổng cộng 181 ngày ở dưới đỉnh cũ. Giai đoạn tiếp theo, bắt đầu từ Jan 2022, có mức giảm nhẹ hơn là 25.4%, nhưng vẫn khiến quỹ nằm dưới đỉnh cũ trong 746 ngày.
Mức giảm và thời gian kéo dài hoàn toàn khác nhau. Một đợt giảm mạnh nhưng nhanh chóng phục hồi và một giai đoạn giảm nhẹ kéo dài nhiều năm đều bị rút gọn thành một tỷ lệ phần trăm duy nhất. Đợt sụp đổ tháng 3 năm 2020 là ví dụ điển hình cho trường hợp thứ nhất, còn cách thị trường phục hồi sau các đợt sụp đổ mô tả chặng phục hồi tiếp theo.
Vì sao mức sụt giảm tối đa tệ hơn khi kéo dài kỳ đo
Mức sụt giảm tối đa là một giá trị cực đại, nên việc bổ sung dữ liệu lịch sử chỉ có thể giữ nguyên hoặc khiến mức này sâu hơn. Tại mỗi thời điểm, đỉnh tích lũy trong khoảng thời gian dài hơn ít nhất cũng cao bằng đỉnh trong khoảng thời gian ngắn hơn. Điều này quyết định kết quả về mặt số học. Việc đo cùng một quỹ trong năm khoảng thời gian đều kết thúc vào ngày 31 tháng 7 năm 2026 cho thấy rõ hiệu ứng này.
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 session_date,
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('2016-07-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 session_date
),
scoped AS (
SELECT arrayJoin([1, 2, 3, 5, 10]) AS lookback_years,
session_date,
close_px
FROM daily
),
windowed AS (
SELECT lookback_years, session_date, close_px
FROM scoped
WHERE session_date >= subtractYears(toDate('2026-07-31'), lookback_years)
),
runs AS (
SELECT lookback_years,
session_date,
close_px,
max(close_px) OVER (PARTITION BY lookback_years ORDER BY session_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_peak
FROM windowed
)
SELECT concat(toString(lookback_years), if(lookback_years = 1, ' year', ' years')) AS lookback,
round(100 * max(1 - close_px / running_peak), 1) AS max_drawdown_pct,
formatDateTime(argMax(session_date, 1 - close_px / running_peak), '%b %Y') AS worst_point
FROM runs
GROUP BY lookback_years
ORDER BY lookback_yearsTrong khoảng thời gian 1 year gần nhất, mức giảm tệ nhất được ghi nhận là 9.1%. Kéo dài cùng chuỗi dữ liệu đó đến 10 years, kết quả là 34.2%, với điểm thấp nhất rơi vào Mar 2020. Quỹ không trở nên rủi ro hơn. Mẫu dữ liệu chỉ dài hơn.
Phép tính tương tự cũng làm sai lệch việc so sánh các chiến lược. Một backtest trong ba năm và một backtest trong hai mươi năm có thể mô tả hành vi giống hệt nhau nhưng vẫn báo cáo mức drawdown rất khác nhau. Vì vậy, con số này ít có ý nghĩa nếu không nêu rõ khoảng thời gian đo. Thiên lệch nhìn trước trong backtest đề cập đến một cách khác khiến backtest có vẻ đẹp hơn thực tế.
Mức sụt giảm tối đa so với biến động
Volatility đo quy mô điển hình của một phiên tăng hoặc giảm. Maximum drawdown đo một quỹ đạo giảm cụ thể. Cả hai đều là chỉ số rủi ro, nhưng trả lời những câu hỏi khác nhau. Bảng đặt hai chỉ số cạnh nhau cho tám cổ phiếu quen thuộc trong năm năm tính đến ngày 31 tháng 7 năm 2026, với volatility được quy đổi theo năm từ giá đóng cửa hằng ngày.
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 session_date,
argMax(toFloat64(close), window_start) AS close_px
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('SPY', 'AAPL', 'MSFT', 'KO', 'JNJ', 'CVX', 'VZ', 'PG')
AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2021-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
GROUP BY ticker, session_date
),
runs AS (
SELECT ticker,
session_date,
close_px,
max(close_px) OVER (PARTITION BY ticker ORDER BY session_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_peak,
lagInFrame(close_px, 1) OVER (PARTITION BY ticker ORDER BY session_date
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_close
FROM daily
)
SELECT ticker,
round(100 * max(1 - close_px / running_peak), 1) AS max_drawdown_pct,
round(100 * sqrt(252) * stddevSampIf(close_px / prev_close - 1, prev_close > 0), 1) AS annualized_volatility_pct
FROM runs
GROUP BY ticker
HAVING countIf(prev_close > 0) > 20
ORDER BY max_drawdown_pct DESCHãy đọc hai cột cùng nhau. VZ ghi nhận mức giảm sâu nhất trong nhóm, ở 45.4%, so với volatility quy đổi theo năm là 22.6%. Ở đầu còn lại, KO giảm 20.9% với volatility ở mức 16.7%. Volatility là mức trung bình của mọi phiên trong giai đoạn, và giá trị trung bình có thể làm nhẹ đi tác động của một đợt giảm kéo dài. Drawdown là quỹ đạo tệ nhất mà giá thực tế đã trải qua. Một khoản đầu tư ít biến động nhưng giảm dần trong hai năm vẫn có thể ghi nhận drawdown sâu dù volatility không nổi bật.
Bất thường volatility thấp phân tích diễn biến của các mã ít biến động trong các khoảng thời gian dài, còn xác định quy mô vị thế theo tiêu chí Kelly cho thấy mức drawdown có thể chuyển thành quy mô vị thế như thế nào.
Điều con số không thể cho bạn biết
Có bốn giới hạn cần ghi nhớ:
- Đây là một quỹ đạo đã xảy ra. Cùng một quy trình có thể tạo ra con số tệ hơn nhiều nếu diễn biến thực tế khác đi.
- Con số phụ thuộc vào độ dài mẫu. Vì vậy, chỉ nên so sánh hai mức sụt giảm trong các khoảng thời gian có cùng độ dài.
- Con số phụ thuộc vào tần suất lấy mẫu. Giá đóng cửa cuối tháng che khuất các mức đáy trong phiên và tạo ra con số nhỏ hơn so với giá đóng cửa hằng ngày trong cùng giai đoạn.
- Con số không chứa thông tin về tương lai. Việc đang thấp hơn 30% so với đỉnh không cho biết liệu điểm thứ 31 sẽ xuất hiện vào ngày mai hay không.
Chính giới hạn cuối cùng này khiến một hệ thống tự động cần có chính sách xử lý drawdown thay vì chỉ đặt ngưỡng drawdown. Quy tắc “dừng ở mức 20%” trả lời một câu hỏi nhưng bỏ ngỏ hai câu hỏi khác: các vị thế đang mở được xử lý thế nào khi hệ thống dừng, và bằng chứng nào cho phép hệ thống hoạt động trở lại. Bộ ngắt mạch cho bot giao dịch phân tích cấu trúc của chính sách này. Chi phí của một quy tắc cố định tùy ý sẽ phát sinh trong giai đoạn hồi phục, khi hệ thống dừng gần đáy rồi hoạt động trở lại quá muộn và bỏ lỡ nhịp tăng. Bỏ lỡ những ngày tăng mạnh nhất lượng hóa tác động đó.
Có một chi tiết đo lường cần lưu ý trước khi so sánh hai con số: drawdown tính trên chuỗi giá của một quỹ và drawdown tính trên tài khoản thực sẽ khác nhau khi tiền được nộp vào hoặc rút ra, cũng như khi cổ tức được ghi nhận. Nội dung cách đo lường lợi nhuận hằng tháng giải thích đầy đủ điểm này.
Câu hỏi thường gặp về mức sụt giảm tối đa
Mức sụt giảm tối đa bao nhiêu được xem là hợp lý?
Không có một mức chung cho mọi trường hợp. Cần xác định mốc tham chiếu và khoảng thời gian đo lường. Trong mười năm tính đến ngày 31 tháng 7 năm 2026, mức giảm tệ nhất của SPY so với đỉnh trước đó, tính trên giá đóng cửa hằng ngày, là 34.2%. Một chiến lược có mức sụt giảm nhỏ hơn trong khoảng thời gian ngắn hơn chưa chắc đã an toàn hơn.
Mức sụt giảm tối đa có giống với biến động không?
Không. Biến động mô tả mức thay đổi hằng ngày điển hình. Mức sụt giảm tối đa mô tả chặng giảm tệ nhất từ đỉnh xuống đáy. Một tài sản có biến động thấp nhưng giảm đều trong hai năm có thể có mức sụt giảm sâu hơn một tài sản biến động mạnh nhưng liên tục lập đỉnh mới.
Mất bao lâu để phục hồi sau mức sụt giảm tối đa?
Điều này tùy trường hợp. Phần tăng trở lại thường kéo dài hơn phần giảm. Với SPY kể từ năm 2016, giai đoạn hoàn tất sâu nhất nằm dưới đỉnh trước đó kéo dài 181 ngày theo lịch, tính từ đỉnh đến khi lập đỉnh mới. Trong khoảng thời gian này, tài sản đã giảm trong 33 ngày.
Mức sụt giảm tối đa có dự báo được các khoản lỗ trong tương lai không?
Không. Chỉ số này mô tả một diễn biến đã xảy ra, và khoảng thời gian quan sát càng dài thì mức ghi nhận gần như luôn càng lớn. Nó cho biết chiến lược đã chịu đựng mức giảm nào cho đến nay, nhưng không giới hạn mức giảm có thể xảy ra tiếp theo.
Mọi biểu đồ ở trên đều lưu câu lệnh SQL phía sau. Hãy mở một biểu đồ, thay ticker hoặc khoảng thời gian, rồi thực hiện cùng phép tính mức sụt giảm trên terminal Strasmore.