Vòng lặp chính của bot: nơi tín hiệu gặp thời gian thực

Phái sinh VN30F1MCập nhật 13/09/2026

Backtest chạy trên dữ liệu đứng yên. Vòng lặp thật chạy trên thị trường đang chuyển động, nơi lệnh có thể không khớp và kết nối có thể đứt giữa chừng.

Vòng lặp chính của bot là nơi mọi thứ đã viết ở các bài trước gặp nhau — và cũng là nơi lộ ra khoảng cách giữa backtest với thị trường thật.

Backtest chạy trên dữ liệu đứng yên: mọi nến đã có sẵn, mọi lệnh đều khớp. Vòng lặp thật chạy trên thị trường đang chuyển động, nơi dữ liệu về trễ, lệnh có thể bị từ chối, và kết nối đứt giữa chừng.

Một vòng làm gì

while market_is_open():
    wait_for_next_bar()              # chờ nến đóng, không chạy mỗi giây
    bars = data.recent(lookback=60)  # lấy cửa sổ dữ liệu gần nhất
    if not data.is_valid(bars):
        log.warning("dữ liệu bất thường, bỏ qua vòng này")
        continue

    desired = strategy.signal(bars)          # mua / bán / đứng ngoài
    position = broker.get_position()         # sự thật lấy từ sàn
    if desired == position.side:
        continue                             # đã ở đúng trạng thái, không làm gì

    order = risk.decide(desired, position, account)   # có vào không, vào bao nhiêu
    if order is None:
        log.info("tầng rủi ro chặn lệnh")
        continue

    broker.submit(order)

Mười dòng này chứa bốn quyết định thiết kế quan trọng.

Chờ nến đóng, không chạy mỗi giây. Nến đang chạy còn thay đổi; tín hiệu tính trên nó có thể xuất hiện rồi biến mất trong cùng một nến, khiến bot vào ra liên tục và trả phí cho mỗi lần.

Kiểm tra dữ liệu trước khi tính. Một nến thiếu hoặc sai logic phải bị chặn ở đây, đúng như các phép kiểm ở bài về dữ liệu.

Vị thế lấy từ sàn, không từ biến trong bộ nhớ. Đây là nguyên tắc chống lệch trạng thái, sẽ nói kỹ ở phần sau.

So trạng thái mong muốn với trạng thái thật rồi mới quyết định hành động. Bot không nghĩ theo kiểu "có tín hiệu thì đặt lệnh" mà theo kiểu "tôi đang ở đâu, muốn tới đâu, cần làm gì để đi từ đây tới đó".

Sơ đồ một vòng

Chờ nến đóngtheo khung của chiến lượcDữ liệu hợp lệđủ nến, giá đúng logicbất thường → bỏ vòng nàyHỏi tín hiệumua / bán / đứng ngoàiĐã ở đúng trạng tháiso với vị thế thật trên sànđúng rồi → không làm gìTầng rủi ro duyệtkhối lượng, giới hạn ngàybị chặn → ghi nhật kýĐặt lệnhvà theo dõi tới khi kết thúc

Ba nhánh thoát bên phải quan trọng ngang đường đi chính. Người mới thường chỉ viết đường thẳng từ trên xuống rồi ngạc nhiên khi bot đặt lệnh trùng hoặc đặt lệnh lúc đã chạm giới hạn lỗ ngày.

Nhịp chạy theo giờ thị trường

Với VN30F1M, vòng lặp không chạy đều suốt ngày mà phải biết bốn mốc phiên.

ATO — không vào lệnh mớiVòng lặp chạyNghỉVòng lặp chạyĐóng vị thế09:0010:0011:0012:0013:0014:0015:00

Hai khoảng đáng chú ý. Phiên ATO khớp theo cơ chế định kỳ chứ không khớp ngay, nên phần lớn chiến lược trong ngày chọn không vào lệnh mới ở đây. Hai mươi phút cuối dành cho việc đóng vị thế nếu chiến lược không giữ qua đêm — đóng muộn hơn là rơi vào phiên ATC với giá không kiểm soát được.

Những mốc này phải nằm trong mã dưới dạng quy tắc rõ ràng, không phải trong đầu người vận hành.

Đồng bộ trạng thái: vấn đề thật sự

Câu hỏi phân biệt bot nghiệp dư với bot dùng được: khi tiến trình tắt rồi bật lại, bot có biết mình đang ở đâu không?

Máy chủ khởi động lại, tiến trình bị hệ điều hành kết thúc, bạn cập nhật mã — những việc này chắc chắn xảy ra. Nếu trạng thái chỉ nằm trong bộ nhớ thì mất sạch.

Khởi độngĐọc file lưuHỏi sànĐối chiếuCho chạylệch → dừng, báo động, chờ người

Bước đối chiếu là bước hay bị bỏ nhất. Nếu file lưu nói đang có 2 hợp đồng mà sàn nói có 3, đừng đoán và đừng tự sửa — dừng lại và báo động. Một bot tự "khắc phục" chênh lệch mà nó không hiểu sẽ tạo ra sai lệch lớn hơn.

Nguyên tắc chia việc: sàn là nguồn sự thật về vị thế và lệnh; file lưu giữ những gì sàn không biết — hôm nay đã lỗ bao nhiêu so với giới hạn ngày, lệnh nào thuộc chiến lược nào.

Bốn tình huống phải xử lý

Đây là những thứ sẽ xảy ra, câu hỏi chỉ là khi nào.

Gửi lệnh nhưng mất phản hồi. Lệnh có thể đã vào sổ hoặc chưa. Cách đúng là truy vấn lại trạng thái từ sàn, không phải gửi lại — gửi lại là cách tạo ra vị thế gấp đôi.

Lệnh khớp một phần. Đặt 5 hợp đồng, khớp 2. Vị thế thật là 2, và mọi tính toán phía sau phải dùng con số đó.

Lệnh bị từ chối. Không đủ ký quỹ, sai bước giá, vượt giới hạn vị thế. Bot phải đọc được lý do và phản ứng khác nhau với từng loại, không thử lại vô hạn.

Mất kết nối giữa phiên. Câu hỏi không phải làm sao để không mất, mà là khi kết nối trở lại thì bot làm gì — và câu trả lời là chạy lại đúng trình tự khởi động ở trên.

Hai kiểu vòng lặp

Có hai cách để bot biết khi nào cần hành động, và chọn sai làm phần vận hành khó lên nhiều.

Kiểu hỏi theo nhịp. Bot ngủ, tỉnh dậy theo lịch, hỏi dữ liệu mới, xử lý, rồi ngủ tiếp. Đơn giản nhất, dễ gỡ lỗi nhất, và đủ cho mọi chiến lược khung phút trở lên. Nhược điểm là có độ trễ tối đa bằng đúng nhịp ngủ.

Kiểu nhận đẩy. Bot giữ một kết nối mở, sàn đẩy dữ liệu về ngay khi có. Độ trễ thấp hơn nhiều, nhưng phải xử lý thêm: kết nối đứt thì nối lại, dữ liệu về không theo thứ tự, và trùng lặp gói tin.

Với bot đầu tiên trên thị trường Việt Nam, kiểu hỏi theo nhịp gần như luôn là lựa chọn đúng. Chiến lược khung mười lăm phút không cần biết giá thay đổi trong từng giây, và mỗi thứ bạn thêm vào phần kết nối là một thứ nữa có thể hỏng lúc ba giờ chiều.

Chỉ chuyển sang kiểu nhận đẩy khi đo được rằng độ trễ đang làm mất lợi thế — đo bằng chính nhật ký giao dịch, không phải bằng cảm giác.

Ngắt khẩn cấp

Phải có cách dừng mọi thứ ngay lập tức, và cách đó phải hoạt động kể cả khi bot đã treo.

Cách đơn giản mà hiệu quả: một tệp cờ trên đĩa mà vòng lặp kiểm tra ở đầu mỗi vòng. Tạo tệp đó là bot ngừng vào lệnh mới và đóng vị thế đang có. Kèm theo đó, luôn giữ khả năng đóng vị thế bằng tay qua ứng dụng của công ty chứng khoán — lớp phòng thủ cuối cùng không phụ thuộc vào mã của bạn.

Chạy thử trước khi chạy thật

Vòng lặp viết xong không có nghĩa là sẵn sàng. Ba mức chạy thử nên đi qua theo thứ tự, mỗi mức trả lời một câu hỏi khác.

Chạy trên dữ liệu lịch sử trả lời: logic có đúng không. Vòng lặp gọi cùng hàm tín hiệu, cùng tầng rủi ro, chỉ khác là broker giả lập và dữ liệu đã có sẵn.

Chạy song song với thị trường thật trả lời: hệ thống có chịu nổi dữ liệu thật không. Bot nhận dữ liệu trực tiếp, tính toán, ghi lại lệnh lẽ ra đã đặt, nhưng không gửi gì. Giai đoạn này lộ ra những thứ backtest không thấy: dữ liệu về trễ, kết nối chập chờn, nến thiếu.

Chạy tiền thật khối lượng nhỏ nhất trả lời: khoảng cách giữa giá bot nghĩ nó khớp và giá thật sự khớp là bao nhiêu. Mục tiêu giai đoạn này không phải kiếm lời mà là đo con số đó.

Rút ngắn ba bước này là cách nhanh nhất trả học phí bằng tiền.

Trước khi sang bài sau

Nên làm được: viết vòng lặp chờ nến đóng rồi gọi hàm tín hiệu; lấy vị thế từ broker giả lập và so với trạng thái mong muốn; và chạy lại bot sau khi tắt giữa chừng mà nó nhận ra đúng trạng thái cũ.

Bài tiếp theo nói về nhật ký — thứ phải có trước khi bot chạm vào tiền thật, vì mọi thứ bạn không ghi lại là thông tin mất vĩnh viễn.

Câu hỏi thường gặp

Bot nên chạy mỗi bao lâu một lần?
Theo khung thời gian của chiến lược. Chiến lược khung 15 phút thì bot thức dậy mỗi khi nến 15 phút đóng, không phải mỗi giây — chạy dày hơn cần thiết chỉ tăng rủi ro gọi API quá giới hạn.
Vì sao phải chờ nến đóng?
Vì nến đang chạy còn thay đổi. Tín hiệu tính trên nến chưa đóng có thể xuất hiện rồi biến mất trong cùng một nến, khiến bot vào ra liên tục và trả phí cho mỗi lần.
Khi khởi động lại, bot lấy vị thế từ đâu?
Từ sàn, không phải từ bộ nhớ. Việc đầu tiên sau khi khởi động là truy vấn vị thế và lệnh đang mở rồi đối chiếu với trạng thái đã lưu; lệch thì dừng và báo động.
Có nên dùng nhiều luồng không?
Không cần cho chiến lược khung phút trở lên. Một vòng lặp tuần tự dễ hiểu và dễ gỡ lỗi hơn nhiều; đa luồng chỉ đáng khi thật sự chạm giới hạn tốc độ.

Đọc tiếp