• 24ºC, Clear
  • Wednesday, 21st January, 2026
  • Trang Chủ
  • Bộ Lọc
  • Nhận Định Thị Trường
  • Phân Tích Doanh Nghiệp
  • Amibroker
    • Code AFL
    • Tự học Amibroker
  • Kiến Thức Đầu Tư
    • Chỉ Báo Kỹ Thuật
    • Phân Tích Cơ Bản
    • Phương Pháp Sóng Elliot
    • Phương Pháp Wyckoff/VSA
    • Vĩ Mô Liên Thị Trường
  • Liên hệ
  • Giới Thiệu
Mới Nhất
  • Phân tích VN-Index ngày 11/08
  • Phân tích HAH
  • Bài 46: Trở thành nhà giao dịch chuyên nghiệp
  • Bài 45: GFX Functions – Bảng trạng thái giao dịch Amibroker
  1. Trang chủ
  2. Amibroker
  3. Tự học Amibroker
  4. Bài 35: BackTesting – Xây Dựng Chiến Lược Giao Dịch Trong Ngày
Bài 35: BackTesting – Xây Dựng Chiến Lược Giao Dịch Trong Ngày

Xem nhanh:

  • Xây Dựng Chiến Lược Giao Dịch Trong Ngày (Intraday) Với Amibroker AFL: Tìm Kiếm Nến “Khổng Lồ” Và Tín Hiệu Đảo Chiều
  • 1. Logic Chiến Lược “Nến Khổng Lồ Đảo Chiều”
  • 2. Lập Trình Chiến Lược Trong Amibroker AFL
  • 3. Đánh Giá Kết Quả Backtest Và Tối Ưu Hóa Thận Trọng

Xây Dựng Chiến Lược Giao Dịch Trong Ngày (Intraday) Với Amibroker AFL: Tìm Kiếm Nến “Khổng Lồ” Và Tín Hiệu Đảo Chiều

Chào các bạn! Việc phát triển hệ thống giao dịch trong ngày (intraday trading systems) là một thách thức thú vị, đòi hỏi khả năng phản ứng nhanh và phân tích hành vi giá trong thời gian thực. Hôm nay, chúng ta sẽ khám phá một chiến lược intraday dựa trên ý tưởng từ cuốn sách “Secret of the Boss” của Frank Ochoa, tập trung vào việc xác định các thanh nến “khổng lồ” và tín hiệu đảo chiều ngay từ đầu phiên giao dịch.

1. Logic Chiến Lược “Nến Khổng Lồ Đảo Chiều”

Ý tưởng cơ bản là tìm kiếm những thanh nến có thân rất lớn xuất hiện sớm trong ngày, sau đó chờ đợi một tín hiệu đảo chiều xác nhận xu hướng ngược lại.

Quy tắc:

  • Khung thời gian: 5 phút.

  • Thời gian giao dịch: Chỉ tập trung vào 3 thanh nến đầu tiên của ngày (ví dụ: 9:15, 9:20, 9:25 AM nếu thị trường mở lúc 9:15 AM).

  • Điều kiện TÍN HIỆU ĐẢO CHIỀU (chỉ áp dụng cho tín hiệu BÁN/SHORT):

    1. Xác định Nến Lớn (First Bar):
      • Là thanh nến đầu tiên của ngày giao dịch.
      • Nến phải là nến xanh (tăng giá).
      • Kích thước (High – Low) của nến phải lớn hơn một bội số nhất định (ví dụ: gấp 2-5 lần) so với phạm vi trung bình của 10 thanh nến gần nhất (trừ thanh nến hiện tại).
      • Giá đóng cửa (Close) của nến phải nằm trong khoảng giữa (ví dụ: không quá gần đỉnh hoặc đáy) để cho thấy tiềm năng đảo chiều chứ không phải là một nến quá mạnh mẽ.
      • Giá mở cửa (Open) của nến không quá cao, cho thấy không có một khoảng trống tăng giá quá lớn.
    2. Xác nhận ĐẢO CHIỀU (Second Bar):
      • Thanh nến thứ hai (hoặc thứ ba) phải là nến đỏ (giảm giá).
      • Giá cao nhất (High) của nến đỏ phải thấp hơn giá cao nhất của nến xanh “khổng lồ” trước đó.
      • Giá thấp nhất (Low) của nến đỏ phải cao hơn một mức trung bình thấp của nến xanh “khổng lồ” (để đảm bảo không có quá ít biên độ lợi nhuận).
  • Lệnh BÁN (SHORT): Được kích hoạt khi tất cả các điều kiện trên được thỏa mãn. Giá bán là giá đóng cửa của thanh nến xác nhận tín hiệu.

  • Điểm Thoát Lệnh (Exit Strategy):

    • Cắt lỗ (Stop Loss): Giá cao nhất (High) của thanh nến xanh “khổng lồ” đầu tiên.
    • Chốt lời (Target): Giá thấp nhất (Low) của thanh nến xanh “khổng lồ” đầu tiên.
    • Thoát cuối ngày (End-of-Day Exit): Thoát vị thế tại thanh nến cuối cùng của phiên giao dịch (quan trọng cho intraday).

2. Lập Trình Chiến Lược Trong Amibroker AFL

Để hiện thực hóa logic này, chúng ta cần sử dụng các hàm sau trong AFL:

  • TimeNum(): Lấy giá trị thời gian của thanh nến (ví dụ: 91500 cho 9:15 AM). Rất quan trọng để giới hạn giao dịch trong 3 nến đầu tiên.
  • Day(): Lấy giá trị ngày. Dùng để xác định thanh nến đầu tiên của một ngày mới (Day() != Ref(Day(), -1)).
  • Ref(Array, offset): Dịch chuyển mảng dữ liệu. Cực kỳ hữu ích để so sánh giá trị hiện tại với giá trị của các thanh nến trước đó.
  • Avg(Array, Periods): Tính giá trị trung bình đơn giản của một mảng trong một số chu kỳ.
  • Hàm Param(): Cho phép bạn tạo các biến có thể điều chỉnh trong cửa sổ tham số của Amibroker mà không cần sửa code.
  • IIf(condition, true_value, false_value): Hàm If-Else cơ bản trong AFL.
  • SetPositionSize(): Để quản lý quy mô vị thế (chúng ta sẽ dùng 2% rủi ro mỗi giao dịch).
  • Các hàm SetStopLoss(), SetProfitTarget(), SetEndBarStop(): Để định nghĩa điểm thoát lệnh.


//_SECTION_BEGIN("Hệ thống giao dịch trong ngày");


// Tham số có thể điều chỉnh (Từ đoạn mã của bạn)
CandleMultiple = Param("Hệ số nhân kích thước nến", 2, 1, 5, 1); // Hệ số nhân để xác định nến lớn
RiskPercentPerTrade = Param("% rủi ro trên mỗi giao dịch", 2, 0.5, 5, 0.5); // % vốn rủi ro mỗi giao dịch
PositionRisk = Param("% rủi ro vị thế", 2, 0.5, 5, 0.5); // % vốn chủ sở hữu rủi ro trong một vị thế (Từ hình ảnh)


// 1. Xác định thanh nến đầu tiên trong ngày (Từ đoạn mã của bạn và hình ảnh)
newday = Day() != Ref(Day(), -1); // Xác định ngày mới
endday = Day() != Ref(Day(), 1); // Xác định cuối ngày (Để thoát lệnh) (Từ hình ảnh)

// 2. Xác định Nến Xanh "Khổng Lồ" (First Bar) (Từ đoạn mã của bạn và hình ảnh)
greencandle = Close > Open; // Nến xanh (C > 0 trong hình ảnh)

// Điều kiện thời gian cho 3 nến đầu tiên
IsFirstBar = newday AND TimeNum() == 091500; // Nến 9:15
IsSecondBar = TimeNum() == 092000; // Nến 9:20
IsThirdBar = TimeNum() == 092500; // Nến 9:25

// Định nghĩa thanh nến tín hiệu đầu tiên (nến xanh lớn) (Từ đoạn mã của bạn)
AverageRangeLast10 = Ref(Avg(High - Low, 10), -1); // Dùng Ref(-1) để không bao gồm nến hiện tại
IsLargeCandle = (High - Low) > (AverageRangeLast10 * CandleMultiple); // candlesize (Từ hình ảnh)

// Các điểm kiểm soát kích thước nến xanh lớn (Từ đoạn mã của bạn và hình ảnh)
mid = (H + L) / 2; // (H+L)/2 (Từ hình ảnh)
highmiddlemid = (High + mid) / 2; // (High+mid)/2 (Từ hình ảnh)
highfinalmid = (High + highmiddlemid) / 2; // (High+highmiddlemid)/2 (Từ hình ảnh)

// Điều kiện đóng/mở cửa của nến FirstBar (Từ đoạn mã của bạn và hình ảnh)
candleclose = Close < highfinalmid AND Close > mid; // Điều kiện đóng cửa (Từ hình ảnh)
candleopen = Open < (Low + ((Low + mid)/2))/2; // Điều kiện mở cửa (Từ hình ảnh)

// Kết hợp logic `firstbar` từ hình ảnh và đoạn mã bạn đã cung cấp:
// firstbar = iif(newday and candlesize and candleclose and candleopen and greencandle,True,False); (Từ hình ảnh)
FirstBarSignal = IIf(newday AND IsLargeCandle AND greencandle AND candleclose AND candleopen, True, False);

// Lưu trữ các giá trị của First Bar (FBH, FBM, FBL - Từ hình ảnh)
FBH = ValueWhen(FirstBarSignal, High); // Giá cao nhất của First Bar
FBM = ValueWhen(FirstBarSignal, mid); // Giá giữa của First Bar (sử dụng 'mid' như trong hình ảnh)
FBL = ValueWhen(FirstBarSignal, Low); // Giá thấp nhất của First Bar

// 3. Định nghĩa Tín hiệu BÁN (SELL) - Trên thanh nến thứ 2/3 xác nhận đảo chiều (Từ hình ảnh và đoạn mã của bạn)
secondbar = TimeNum() == 092500; // Trong hình ảnh ghi 092500 cho 'secondbar'. Có thể là thời gian của nến xác nhận.
// Giả sử nó là điều kiện thời gian cho nến xác nhận (tức là nến thứ 3: 092500 theo code bạn cung cấp)
// Nếu `secondbar` trong hình ảnh thực sự có nghĩa là nến thứ 2 (092000), cần điều chỉnh lại.
// Dựa trên code trước của bạn, IsSecondBar là 092000 và IsThirdBar là 092500.
// Chúng ta sẽ sử dụng `IsSecondBar OR IsThirdBar` cho khung thời gian xác nhận như trong code bạn đã cung cấp,
// và `secondbar` từ hình ảnh như một điều kiện thời gian cụ thể cho một trong các điều kiện xác nhận.

sellcont = Flip(FirstBarSignal, secondbar); // Từ hình ảnh: sellcont = Flip(firstbar, secondbar);

// Điều kiện cho Selling Candle (thanh nến xác nhận) (Từ hình ảnh)
// Short = sellcont AND C < 0 AND High < FBH AND Low > FBM; (Từ hình ảnh)
// C < 0 có lẽ là `Close < Open` (nến đỏ).
IsRedCandle = Close < Open;

Short = sellcont AND IsRedCandle AND High < FBH AND Low > FBM; // Tín hiệu Bán cuối cùng dựa trên hình ảnh

// Chỉ chấp nhận tín hiệu trong 3 nến đầu ngày (Từ logic code của bạn)
Short = Short AND (IsSecondBar OR IsThirdBar); // Giả sử xác nhận trong nến thứ 2 hoặc thứ 3 (092000 hoặc 092500)

// 4. Thiết lập Giá vào lệnh và Thoát lệnh (Dựa trên hình ảnh và thông lệ của Amibroker)
ShortPrice = ValueWhen(Short, Close); // ShortPrice = ValueWhen(Short,Close); (Từ hình ảnh)

// Logic 'cover' (Từ hình ảnh)
// Cover = Cross(High,FBH) OR Cross(FBL,Low) OR endday;
// CoverPrice = IIf(Cross(High,FBH), FBH, IIf(Cross(FBL,Low),FBL,Close));
// Logic 'cover' này phức tạp và dường như định nghĩa các điểm thoát lệnh.

// Chúng ta sẽ diễn giải 'Cover' như một tín hiệu thoát lệnh.
// Cross(High, FBH): Giá cao cắt lên trên FBH (dừng lỗ)
// Cross(FBL, Low): Giá thấp cắt xuống dưới FBL (chốt lời)
// endday: Thoát lệnh cuối ngày.
Cover = Cross(High,FBH) OR Cross(FBL,Low) OR endday;

// CoverPrice: Giá tại thời điểm thoát lệnh.
CoverPrice = IIf(Cross(High,FBH), FBH, IIf(Cross(FBL,Low),FBL,Close));

// Biến 'short' từ hình ảnh là `short = ExRem(short,Cover);`
short = ExRem(Short, Cover); // Sử dụng 'Cover' mới định nghĩa để loại trừ.

// 5. Quản lý Quy mô Vị thế (Rủi ro trên mỗi giao dịch) (Từ hình ảnh)
// pointrisk = FBH - ShortPrice;
// percentrisk = (pointrisk/ShortPrice) * 100;
// PositionRisk = 2; // how much (in percent of equity) we risk in single position
// TradeRisk = percentrisk; // trade risk in percent equals to max. loss percentage stop
// PctSize = (PositionRisk / TradeRisk)*100;

pointrisk = FBH - ShortPrice; // Khoảng cách rủi ro tính bằng điểm
percentrisk = (pointrisk / ShortPrice) * 100; // Chuyển sang % rủi ro

TradeRisk = percentrisk; // Rủi ro giao dịch tính bằng phần trăm bằng với phần trăm dừng lỗ tối đa
PctSize = (PositionRisk / TradeRisk) * 100; // % vốn để phân bổ cho lệnh này

SetPositionSize(PctSize, spsPercentOfEquity); // Từ hình ảnh

// 6. Cài đặt Stop Loss, Profit Target, và Thoát lệnh cuối ngày (Dựa trên logic Cover của hình ảnh)
// Logic 'Cover' đã bao hàm dừng lỗ (Cross(High,FBH)) và chốt lời (Cross(FBL,Low))
// Các hàm SetStopLoss/SetProfitTarget tích hợp sẵn của Amibroker thường được ưu tiên để rõ ràng và mạnh mẽ hơn.
// Nếu chúng ta muốn sử dụng `Cover` làm cơ chế thoát chính, chúng ta định nghĩa điều kiện Thoát.
Exit = Cover;
ExitPrice = CoverPrice;

SetTradeDelays(0,0,0,0); // Không có độ trễ

// 7. Lọc tín hiệu trùng lặp (Từ code của bạn)
// Short = ExRem(Short, Buy); // Nếu bạn có tín hiệu Buy, hãy sử dụng dòng này. Nếu không, chỉ cần ExRem(Short, 0);
// Giả sử không có tín hiệu `Buy` được định nghĩa trong chiến lược cụ thể này để `ExRem` lọc.
// Hình ảnh đã có `short = ExRem(short,Cover);`. Chúng ta sẽ sử dụng nó cho biến `short`.

// 8. Vẽ tín hiệu trên biểu đồ (Tùy chọn)
Plot(Close, "Giá đóng cửa", colorBlack, styleCandle);
PlotShapes(IIf(Short, shapeDownArrow, shapeNone), colorRed, 0, High, -20); // Vẽ mũi tên xuống khi có tín hiệu bán

//_SECTION_END();

Lưu ý quan trọng khi lập trình và backtest:

  • TimeNum(): Cần cẩn thận với múi giờ và cách Amibroker lưu trữ thời gian (start/end of interval). Luôn kiểm tra trong Tools -> Preferences -> Intraday để đảm bảo cài đặt thời gian khớp với logic của bạn.
  • Ref(Array, offset): Khi tính toán AverageRangeLast10, việc sử dụng Ref(Avg(..., 10), -1) là rất quan trọng để đảm bảo bạn không bao gồm dữ liệu của thanh nến hiện tại vào phép tính trung bình.
  • Backtesting Settings:
    • Commission/Slippage: Luôn bao gồm các khoản phí này (ví dụ: 0.045%). Điều này làm cho kết quả backtest thực tế hơn nhiều.
    • Initial Equity: Vốn ban đầu.
    • Futures Mode: Nếu giao dịch Futures.
    • Stop/Target type: Sử dụng Price khi bạn đã định nghĩa Stop Loss/Profit Target bằng mức giá cụ thể trong code AFL.

3. Đánh Giá Kết Quả Backtest Và Tối Ưu Hóa Thận Trọng

Trong ví dụ này, chiến lược được backtest trên dữ liệu 1 phút từ 2012-2018 cho một số mã cổ phiếu cho thấy kết quả thua lỗ (ví dụ: -6.22%). Điều này là bình thường và quan trọng là cách bạn phân tích nó:

  • Không phải mọi chiến lược đều có lợi nhuận: Mục tiêu chính là học cách xây dựng, backtest và phân tích.
  • Số lượng giao dịch: Một hệ thống cần ít nhất 800-1000 giao dịch để có kết quả thống kê đáng tin cậy. Nếu số lượng giao dịch quá ít, kết quả backtest có thể không phản ánh đúng tiềm năng của hệ thống.
  • Drawdown (Sụt giảm vốn): Hãy đặc biệt chú ý đến Max Drawdown và hình dạng của Equity Curve. Một chiến lược liên tục giảm vốn qua các năm hoặc có những đợt sụt giảm dài và sâu sẽ không thể duy trì được về mặt tâm lý, dù bạn có bao nhiêu tiền.
  • Tối ưu hóa thận trọng (Playing with Parameters):
    • Bạn có thể thay đổi các tham số như CandleMultiple (ví dụ: từ 2 lên 5) để xem tác động đến hiệu suất.
    • Một thay đổi nhỏ có thể tạo ra sự khác biệt lớn (ví dụ: giảm lỗ từ -$3000 xuống -$800).
    • Cảnh báo: Đừng “over-optimize” (tối ưu hóa quá mức) một chiến lược để nó trông hoàn hảo trên dữ liệu quá khứ. Điều này thường dẫn đến các hệ thống không hoạt động trong thị trường thực. Mục tiêu là tìm ra các tham số ổn định và mạnh mẽ chứ không phải “hoàn hảo”.
  • trancotam
❮ Prev Next ❯
Chia sẻ: Facebook WhatsApp Twitter
Chia sẻ với ứng dụng khác

Tin Xem Nhiều

Bài 1: Chiến Lược Bảo Vệ Vốn Hiệu Quả Khi Đầu Tư (Phần 1)
Chỉ Báo Kỹ Thuật
Bài 1: Chiến Lược Bảo Vệ Vốn Hiệu Quả Khi Đầu Tư…
21 Tháng 5, 2025
Phân tích BFC
Phân Tích Doanh Nghiệp
Phân tích BFC
14 Tháng 7, 2025
Bài 46: Trở thành nhà giao dịch chuyên nghiệp
Tự học Amibroker
Bài 46: Trở thành nhà giao dịch chuyên nghiệp
22 Tháng 6, 2025
Phân tích HAH
Phân Tích Doanh Nghiệp
Phân tích HAH
15 Tháng 7, 2025
Bảng công cụ phân tích thị trường
Chưa phân loại
Bảng công cụ phân tích thị trường
13 Tháng 5, 2025
Donation
Phóng to ảnh

TADOSO là nền tảng chia sẻ kiến thức và kinh nghiệm đầu tư tài chính, đặc biệt trong lĩnh vực chứng khoán, nhằm kết nối và nâng cao tư duy cho nhà đầu tư cá nhân tại Việt Nam. Với triết lý "Tạo dựng – Đồng hành – Sở hữu", TADOSO hướng đến xây dựng cộng đồng học hỏi minh bạch, trung lập, không bị chi phối bởi tổ chức tài chính nào.

Newsletter

Hãy để lại mail để chúng tôi có thể phục vụ bạn tốt hơn

Donation - Buy me a coffee

Donation

Copyright © All rights reserved | This template is made with by Tran Co Tam

  • Terms of use
  • Privacy Policy
  • Contact
Zalo Messenger
×

Liên hệ