Trang chủBlogChiến Lược Nâng CaoSignal Monitoring kiểm tra số lượng và phân phối tín hiệu có khác lịch sử bất thường không?
Signal Monitoring kiểm tra số lượng và phân phối tín hiệu có khác lịch sử bất thường không?
Chiến Lược Nâng Cao

Signal Monitoring kiểm tra số lượng và phân phối tín hiệu có khác lịch sử bất thường không?

Signal Monitoring

Ngọc Hải LươngNgọc Hải Lương
16/8/2026
10 phút phút đọc
31 lượt xem

Một strategy chạy live không chỉ cần theo dõi P&L. Trước khi lợi nhuận xấu đi, nhiều hệ thống đã phát tín hiệu bất thường từ khá sớm: số lượng cổ phiếu được chọn tăng đột biến, score của toàn universe dịch chuyển, một sector chiếm phần lớn tín hiệu, confidence của model giảm hoặc tín hiệu gần như biến mất. Signal Monitoring là quá trình kiểm tra xem tín hiệu mà model đang tạo ra hôm nay có còn giống những gì từng xuất hiện trong lịch sử hay không. Với đầu tư định lượng, đây là một lớp kiểm soát rất thực tế vì đôi khi model vẫn chạy bình thường, không báo lỗi, nhưng đầu vào hoặc cấu trúc thị trường đã thay đổi đủ lớn để output không còn giống trạng thái mà strategy từng được backtest.

e1206627-f090-4dfa-8d69-50592d38176d.png

Phần 1. Đừng chỉ hỏi strategy đang lãi hay lỗ, hãy kiểm tra tín hiệu hôm nay có “bình thường” không

Giả sử một strategy momentum trên VN30 thường tạo khoảng 4–8 tín hiệu mua mỗi tuần. Một ngày hệ thống đột nhiên phát 26 tín hiệu mua. Không có lỗi Python, dữ liệu vẫn tải được và model vẫn chạy. Nhưng 26 tín hiệu là một trạng thái rất khác với lịch sử. Nguyên nhân có thể đơn giản là toàn thị trường vừa breakout, nhưng cũng có thể do một feature bị lỗi, một threshold không còn phù hợp hoặc dữ liệu đầu vào bị lệch.
Metric đầu tiên rất dễ theo dõi là số lượng tín hiệu:

signal_count = signals.sum(axis=1)

Sau đó so với lịch sử gần nhất:

mean_count = signal_count.rolling(60).mean()
std_count = signal_count.rolling(60).std()
signal_count_z = (
    signal_count - mean_count
) / std_count

Ví dụ bình thường strategy tạo trung bình 6 tín hiệu/ngày với độ lệch chuẩn 3. Hôm nay xuất hiện 18 tín hiệu:

Signal Count = 18
Historical Mean = 6
Historical Std = 3
Z-score = 4.0

Một Z-score bằng 4 cho thấy số lượng tín hiệu đang nằm rất xa trạng thái thông thường. Điều này chưa có nghĩa phải tắt strategy, nhưng đủ để hệ thống gắn cờ kiểm tra.
Số lượng tín hiệu chỉ là lớp đầu tiên. Một strategy có thể vẫn tạo đúng 6 tín hiệu nhưng phân phối score bên dưới đã thay đổi. Giả sử model chấm mỗi cổ phiếu từ 0 đến 1 và mua khi score > 0.7. Trong quá khứ score của universe thường có dạng:

Median score      0.48
90th percentile   0.71
Max score         0.86

Nhưng hôm nay:

Median score      0.66
90th percentile   0.84
Max score         0.97

Số lượng cổ phiếu vượt threshold chắc chắn sẽ tăng, nhưng vấn đề sâu hơn là toàn bộ distribution của model đã dịch lên. Đây có thể là một market regime rất mạnh, nhưng cũng có thể do feature scale thay đổi.
Có thể theo dõi các percentile đơn giản:

score_summary = pd.DataFrame({
    "median": scores.median(axis=1),
    "p75": scores.quantile(0.75, axis=1),
    "p90": scores.quantile(0.90, axis=1),
    "max": scores.max(axis=1)
})

Nếu strategy dùng Relative Strength, Momentum, Volume Z-score hay breakout score, việc phân phối của toàn universe dịch chuyển thường có ý nghĩa khá lớn. Ví dụ trong một bull market mạnh, rất nhiều cổ phiếu cùng có Momentum60D dương. Một threshold cố định Momentum > 10% có thể từ chỗ chọn 10% universe thành chọn 60% universe. Model không hỏng; thị trường đã đổi trạng thái.
Một metric nữa rất hữu ích là tỷ lệ tín hiệu trên toàn universe:

signal_rate = (
    signal_count /
    number_of_available_stocks
)

Nếu bình thường 8% cổ phiếu được chọn nhưng hôm nay 42% universe cùng phát tín hiệu, strategy đang ở một trạng thái hiếm. Với thị trường Việt Nam, đây đôi khi chính là thông tin về breadth. Nếu hàng loạt bank, chứng khoán, bất động sản và mid-cap cùng vượt momentum threshold, đó có thể là một broad risk-on regime. Ngược lại, nếu VN-Index tăng nhưng signal rate giảm từ 35% xuống 12%, leadership đang thu hẹp.
Signal Monitoring vì vậy không chỉ là kiểm tra model. Nó còn có thể trở thành một cách quan sát cấu trúc thị trường.

Phần 2. Bất thường không chỉ nằm ở số tín hiệu mà còn ở sector, score và cấu trúc của tín hiệu

Giả sử một strategy thường chọn khoảng 15 cổ phiếu từ nhiều nhóm ngành. Hôm nay vẫn có đúng 15 tín hiệu nên Signal Count nhìn hoàn toàn bình thường. Nhưng 12/15 tín hiệu lại thuộc nhóm ngân hàng. Portfolio lúc này không còn giống trạng thái bình thường dù tổng số signal không thay đổi.
Có thể tính concentration rất đơn giản:

sector_share = (
    signal_df
    .groupby("sector")["signal"]
    .sum()
)
sector_share = sector_share / sector_share.sum()

Nếu một sector chiếm:

Bank        72%
Securities  12%
Technology   8%
Others       8%

trong khi lịch sử Bank thường chỉ chiếm 20–30%, signal distribution đã thay đổi đáng kể.
Điều này đặc biệt quan trọng với thị trường Việt Nam vì nhiều giai đoạn leadership tập trung rất mạnh vào một số nhóm. Một model có thể chọn 20 cổ phiếu và nhìn bề ngoài khá diversified, nhưng nếu 14 mã đều là bank hoặc chứng khoán, portfolio vẫn đang đặt cược vào cùng một nguồn rủi ro.
Có thể đo concentration bằng Herfindahl Index:

hhi = (sector_share ** 2).sum()

HHI càng cao, tín hiệu càng tập trung vào ít nhóm.
Một lớp khác là kiểm tra distribution shift của chính feature. Giả sử strategy dùng:

Momentum60D
Relative Strength
Volume Z-score
Volatility20D
Distance MA50

Nếu model được train trong giai đoạn Volume Z-score thường dao động quanh 0 nhưng gần đây distribution liên tục lệch về +1 đến +2 vì thanh khoản toàn thị trường tăng mạnh, threshold cũ có thể không còn mang ý nghĩa giống trước.
Một cách dễ hiểu là so median hoặc percentile hiện tại với lịch sử. Nếu muốn chặt hơn có thể dùng PSI, Population Stability Index. PSI thường được dùng để đo distribution hiện tại đã khác distribution tham chiếu tới mức nào.
Ý tưởng rất đơn giản. Chia một feature thành các bucket lịch sử, ví dụ:

Bucket 1: thấp nhất 20%
Bucket 2: 20–40%
Bucket 3: 40–60%
Bucket 4: 60–80%
Bucket 5: cao nhất 20%

Trong lịch sử mỗi bucket có khoảng 20% quan sát. Nếu hiện tại distribution thành:

Bucket 1     5%
Bucket 2     8%
Bucket 3    12%
Bucket 4    25%
Bucket 5    50%

feature đã dịch chuyển rất mạnh về phía cao.
Có thể xây PSI bằng Python:

import numpy as np
def psi(expected, actual):
    expected = np.clip(expected, 1e-6, None)
    actual = np.clip(actual, 1e-6, None)
    return np.sum(
        (actual - expected)
        * np.log(actual / expected)
    )

Không cần biến PSI thành một rule tuyệt đối. Giá trị của nó nằm ở việc giúp nhận ra model đang được đưa một loại dữ liệu khác đáng kể so với dữ liệu từng dùng để research.
Một ví dụ rất thực tế là strategy breakout. Bình thường:

Breakout signals/day      5
Median Volume Z           1.4
Median RS20               +3%
Average ATR               2.1%

Trong một giai đoạn thị trường rất nóng:

Breakout signals/day     24
Median Volume Z           2.6
Median RS20              +9%
Average ATR               4.8%

Nếu chỉ nhìn P&L, strategy có thể vẫn đang kiếm tiền rất tốt. Nhưng signal distribution cho biết strategy đang hoạt động trong một environment rất khác lịch sử. Khi regime đảo chiều, số lượng position lớn và volatility cao có thể khiến portfolio chịu drawdown nhanh hơn nhiều.
Signal Monitoring vì vậy nên theo dõi cả bốn lớp: signal count, signal rate, score distribution và concentration. Nếu cả bốn cùng thay đổi, khả năng đây không còn là noise thông thường.

Phần 3. Signal Monitoring nên được dùng như hệ thống cảnh báo trước khi P&L có vấn đề

Một dashboard Signal Monitoring thực tế không cần quá phức tạp. Có thể chỉ cần:

Signals Today             18
Historical Avg             7
Signal Count Z-score      3.2
Signal Rate               31%
Historical Signal Rate    12%
Score Median              0.67
Historical Median         0.49
Largest Sector Share      58%
Feature PSI               0.29

Mỗi metric có thể gắn trạng thái:

NORMAL
WATCH
ABNORMAL

Ví dụ:

if abs(signal_count_z) < 2:
    status = "NORMAL"
elif abs(signal_count_z) < 3:
    status = "WATCH"
else:
    status = "ABNORMAL"

Không nên để một metric duy nhất tự động tắt strategy. Một signal count Z-score = 3 có thể chỉ phản ánh một phiên breakout diện rộng. Nhưng nếu đồng thời xuất hiện:

Signal Count Z-score > 3
Feature PSI cao
Sector Concentration cao
Volatility tăng mạnh

thì khả năng môi trường hiện tại khác đáng kể so với historical regime sẽ lớn hơn.
Có thể xây một Monitoring Score:

monitoring_score = (
    count_anomaly
    + distribution_shift
    + concentration_anomaly
    + volatility_anomaly
)

Ví dụ:

0–1 điểm    Normal
2 điểm      Watch
3 điểm      Review
4 điểm      Risk Control

Khi score tăng, hành động không nhất thiết là dừng strategy. Có thể giảm position size, giảm maximum number of positions, kiểm tra dữ liệu đầu vào hoặc yêu cầu manual review trước khi tiếp tục tăng exposure.
Một workflow thực tế có thể là:

Market Data
→ Feature Calculation
→ Model / Signal
→ Signal Monitoring
→ Risk Check
→ Order

Signal Monitoring nằm giữa model và order. Nếu model tạo output bất thường, hệ thống có cơ hội kiểm tra trước khi biến output đó thành tiền thật.
Ví dụ strategy momentum thường có tối đa 10 position. Một ngày signal rate tăng từ 10% lên 45% vì thị trường rất mạnh. Nếu engine đơn giản lấy toàn bộ signal, portfolio có thể tăng từ 10 lên 30 position chỉ trong vài phiên. Một monitoring rule có thể giữ:

max_positions = 10
if signal_count_z > 3:
    max_positions = 6

hoặc tăng threshold:

if signal_environment == "EXTREME":
    entry_threshold = 0.80
else:
    entry_threshold = 0.70

Mục tiêu không phải thay đổi strategy mỗi khi dữ liệu khác lịch sử. Mục tiêu là tránh trường hợp hệ thống tự động tăng risk đúng lúc output của model đang nằm ngoài vùng từng được kiểm nghiệm.
Một use case khác là phát hiện lỗi dữ liệu. Giả sử bình thường strategy tạo 5–10 signal nhưng hôm nay bằng 0. Điều này có thể do thị trường yếu, nhưng cũng có thể do một cột feature toàn NaN.
Có thể thêm:

missing_rate = features.isna().mean()

Nếu:

Signal Count = 0
Missing Feature Rate = 67%

đây gần như chắc chắn là vấn đề data pipeline chứ không phải market regime.
Ngược lại:

Signal Count = 0
Missing Rate = 0%
Momentum Distribution rất thấp
Breadth yếu
VN-Index dưới MA200

thì việc không có signal có thể chính là output đúng của model.
Điểm quan trọng là Signal Monitoring giúp tách hai câu chuyện rất khác nhau: model đang phản ứng hợp lý với một thị trường khác thường, hay hệ thống đang tạo tín hiệu sai vì dữ liệu/model bị lỗi.
Một cách kiểm tra rất hay là lưu snapshot mỗi ngày:

Date
Signal Count
Signal Rate
Score Median
Score P90
Sector Concentration
Feature PSI
Market Volatility
VN-Index Return
Portfolio Exposure

Sau một thời gian, chính bảng monitoring này trở thành một dataset mới. Có thể quay lại xem trước những giai đoạn drawdown lớn của strategy, Signal Count, PSI hay concentration có thường thay đổi trước hay không.
Ví dụ nếu nhiều lần trước drawdown lớn đều xuất hiện:

Signal Count ↑
Sector Concentration ↑
Volatility ↑
Breadth ↓

thì Signal Monitoring không còn chỉ là kiểm tra vận hành. Nó bắt đầu trở thành một lớp Risk Management cho strategy.
Một Quant system tốt vì vậy không nên chỉ hỏi “model hôm nay phát tín hiệu gì?”. Nó nên hỏi thêm “output hôm nay có giống loại output mà model từng tạo trong quá khứ không?”. Nếu một strategy được backtest chủ yếu trong trạng thái 5–10 signal mỗi ngày nhưng live đột nhiên tạo 30–40 signal, đó là thông tin cần được xử lý trước khi nhìn thấy hậu quả trên P&L.
Signal Monitoring không giúp strategy tìm được nhiều alpha hơn. Nó giúp phát hiện khi strategy đang bước vào một vùng mà lịch sử chưa cho nhiều bằng chứng để tin tưởng. Trong live trading, đó thường là một cảnh báo có giá trị hơn việc đợi đến khi drawdown xuất hiện mới bắt đầu tìm nguyên nhân.