Kiểm tra chiến lược giao dịch
Ý tưởng về giao dịch tự động hấp dẫn bởi thực tế rằng robot giao dịch có thể hoạt động không ngừng nghỉ 24 giờ mỗi ngày, bảy ngày mỗi tuần. Robot không mệt mỏi, không nghi ngờ hay sợ hãi, hoàn toàn không bị ảnh hưởng bởi bất kỳ vấn đề tâm lý nào. Chỉ cần chính thức hóa rõ ràng các quy tắc giao dịch và triển khai chúng trong các thuật toán, robot sẽ sẵn sàng làm việc không mệt mỏi. Nhưng trước tiên, bạn phải đảm bảo rằng hai điều kiện quan trọng sau được đáp ứng:
- Expert Advisor thực hiện các hoạt động giao dịch theo quy tắc của hệ thống giao dịch;
- Chiến lược giao dịch, được triển khai trong EA, cho thấy lợi nhuận trên lịch sử.
Để trả lời những câu hỏi này, chúng ta sử dụng Strategy Tester, được tích hợp trong terminal client MetaTrader 5.
Phần này bao gồm các tính năng của việc kiểm tra và tối ưu hóa chương trình trong Strategy Tester:
- Giới hạn hàm trong Strategy Tester
- Chế độ tạo tick
- Mô phỏng spread
- Sử dụng tick thực trong quá trình kiểm tra
- Biến toàn cục của terminal client
- Tính toán chỉ báo trong quá trình kiểm tra
- Tải lịch sử trong quá trình kiểm tra
- Kiểm tra đa tiền tệ
- Mô phỏng thời gian trong Strategy Tester
- Đối tượng đồ họa trong kiểm tra
- Hàm
OnTimer()
trong Strategy Tester - Hàm
Sleep()
trong Strategy Tester - Sử dụng Strategy Tester cho các bài toán tối ưu hóa trong tính toán toán học
- Đồng bộ hóa thanh trong chế độ "Chỉ giá mở"
- Hàm
IndicatorRelease()
trong Tester - Xử lý sự kiện trong Tester
- Tác nhân kiểm tra
- Trao đổi dữ liệu giữa terminal và tác nhân
- Sử dụng thư mục chung của tất cả các terminal client
- Sử dụng DLL
Giới hạn bộ nhớ và không gian đĩa trong Mạng MQL5 Cloud
Giới hạn sau áp dụng cho các tối ưu hóa chạy trong Mạng MQL5 Cloud: Expert Advisor không được ghi lên đĩa quá 4GB thông tin hoặc sử dụng quá 4GB RAM. Nếu vượt quá giới hạn, tác nhân mạng sẽ không thể hoàn thành tính toán chính xác, và bạn sẽ không nhận được kết quả. Tuy nhiên, bạn sẽ bị tính phí cho toàn bộ thời gian đã sử dụng cho các tính toán.
Nếu bạn cần lấy thông tin từ mỗi lần tối ưu hóa, hãy gửi khung mà không ghi lên đĩa. Để tránh sử dụng hoạt động tệp trong Expert Advisor trong quá trình tính toán trên Mạng MQL5 Cloud, bạn có thể sử dụng kiểm tra sau:
int handle=INVALID_HANDLE;
bool file_operations_allowed=true;
if(MQLInfoInteger(MQL_OPTIMIZATION) || MQLInfoInteger(MQL_FORWARD))
file_operations_allowed=false;
if(file_operations_allowed)
{
...
handle=FileOpen(...);
...
}
2
3
4
5
6
7
8
9
10
11
Giới hạn hàm trong Strategy Tester
Có những giới hạn hoạt động đối với một số hàm trong Strategy Tester của terminal client.
Hàm Comment()
, Print()
và PrintFormat()
Để tăng hiệu suất, các hàm Comment()
, Print()
và PrintFormat()
không được thực thi khi tối ưu hóa tham số của robot giao dịch. Ngoại lệ là việc sử dụng các hàm này bên trong trình xử lý OnInit()
. Điều này cho phép dễ dàng tìm ra nguyên nhân lỗi khi chúng xảy ra.
Hàm Alert()
, MessageBox()
, PlaySound()
, SendFTP()
, SendMail()
, SendNotification()
, WebRequest()
Các hàm Alert()
, MessageBox()
, PlaySound()
, SendFTP()
, SendMail()
, SendNotification()
và WebRequest()
được thiết kế để tương tác với "thế giới bên ngoài" không được thực thi trong Strategy Tester.
Chế độ tạo tick
Expert Advisor là một chương trình, được viết bằng MQL5, chạy mỗi lần để phản hồi một số sự kiện bên ngoài. EA có một hàm tương ứng (trình xử lý sự kiện) cho mỗi sự kiện được định nghĩa trước.
Sự kiện NewTick
(thay đổi giá) là sự kiện chính cho EA, do đó, chúng ta cần tạo một chuỗi tick để kiểm tra EA. Có 3 chế độ tạo tick được triển khai trong Strategy Tester của terminal client MetaTrader 5:
- Mọi tick
- OHLC 1 phút (giá OHLC với thanh phút)
- Chỉ giá mở
Chế độ "Mọi tick" là cơ bản và chi tiết nhất, hai chế độ còn lại là sự đơn giản hóa của chế độ cơ bản, và sẽ được mô tả so sánh với chế độ "Mọi tick". Xem xét cả ba chế độ để hiểu sự khác biệt giữa chúng.
"Mọi Tick"
Dữ liệu báo giá lịch sử cho các công cụ tài chính được truyền từ máy chủ giao dịch đến terminal client MetaTrader 5 dưới dạng các thanh phút được đóng gói. Thông tin chi tiết về việc xảy ra yêu cầu và xây dựng các khung thời gian cần thiết có thể được lấy từ chương Tổ chức truy cập dữ liệu của Tài liệu tham khảo MQL5.
Yếu tố tối thiểu của lịch sử giá là thanh phút, từ đó bạn có thể lấy thông tin về bốn giá trị của giá:
- Open - giá mà thanh phút được mở;
- High - mức tối đa đạt được trong thanh phút này;
- Low - mức tối thiểu đạt được trong thanh phút này;
- Close - giá đóng của thanh.
Thanh phút mới không được mở vào thời điểm phút mới bắt đầu (số giây bằng 0), mà khi một tick xảy ra - thay đổi giá ít nhất một điểm. Hình ảnh cho thấy thanh phút đầu tiên của tuần giao dịch mới, có thời gian mở là 2011.01.10 00:00. Khoảng cách giá giữa thứ Sáu và thứ Hai, mà chúng ta thấy trên biểu đồ, là phổ biến, vì tỷ giá tiền tệ biến động ngay cả vào cuối tuần để phản ứng với tin tức đến.
Đối với thanh này, chúng ta chỉ biết rằng thanh phút được mở vào ngày 10 tháng 1 năm 2011 lúc 00 giờ 00 phút, nhưng chúng ta không biết gì về giây. Nó có thể đã được mở lúc 00:00:12 hoặc 00:00:36 (12 hoặc 36 giây sau khi bắt đầu ngày mới) hoặc bất kỳ thời điểm nào khác trong phút đó. Nhưng chúng ta biết rằng giá mở của EURUSD là 1.28940 tại thời điểm mở thanh phút mới.
Chúng ta cũng không biết (chính xác đến giây) khi nào chúng ta nhận được tick tương ứng với giá đóng của thanh phút đang xem xét. Chúng ta chỉ biết một điều - giá Close cuối cùng của thanh phút. Đối với phút này, giá là 1.28958. Thời gian xuất hiện của giá High và Low cũng không xác định, nhưng chúng ta biết rằng giá tối đa và tối thiểu lần lượt là 1.28958 và 1.28940.
Để kiểm tra chiến lược giao dịch, chúng ta cần một chuỗi tick, trên đó hoạt động của Expert Advisor sẽ được mô phỏng. Do đó, đối với mỗi thanh phút, chúng ta biết 4 điểm kiểm soát
, nơi giá chắc chắn đã xuất hiện. Nếu một thanh chỉ có 4 tick, thì đây là thông tin đủ để thực hiện kiểm tra, nhưng thường khối lượng tick lớn hơn 4.
Do đó, có nhu cầu tạo thêm các điểm kiểm soát cho các tick xảy ra giữa giá Open, High, Low và Close. Nguyên tắc của chế độ tạo tick "Mọi tick" được mô tả trong Thuật toán tạo tick trong Strategy Tester của Terminal MetaTrader 5, một hình ảnh từ đó được trình bày dưới đây.
Khi kiểm tra ở chế độ "Mọi tick", hàm OnTick()
của EA sẽ được gọi tại mỗi điểm kiểm soát. Mỗi điểm kiểm soát là một tick từ chuỗi được tạo. EA sẽ nhận được thời gian và giá của tick mô phỏng, giống như khi hoạt động trực tuyến.
Quan trọng: Chế độ kiểm tra "Mọi tick" là chính xác nhất, nhưng đồng thời cũng tốn thời gian nhất. Để kiểm tra ban đầu hầu hết các chiến lược giao dịch, thường đủ để sử dụng một trong hai chế độ kiểm tra khác.
"OHLC 1 phút"
Chế độ "Mọi tick" là chính xác nhất trong ba chế độ, nhưng đồng thời là chậm nhất. Việc chạy trình xử lý OnTick()
xảy ra tại mỗi tick, trong khi khối lượng tick có thể khá lớn. Đối với chiến lược mà chuỗi tick của chuyển động giá trong suốt thanh không quan trọng, có một chế độ mô phỏng nhanh hơn và thô hơn - "OHLC 1 phút".
Trong chế độ "OHLC 1 phút", chuỗi tick được xây dựng chỉ bằng giá OHLC của các thanh phút
, số lượng điểm kiểm soát được tạo giảm đáng kể - do đó, thời gian kiểm tra cũng giảm. Việc khởi chạy hàm OnTick()
được thực hiện trên tất cả các điểm kiểm soát, được xây dựng từ giá của các thanh phút OHLC.
Việc từ chối tạo các tick trung gian bổ sung giữa giá Open, High, Low và Close dẫn đến sự xuất hiện của tính xác định cứng nhắc trong sự phát triển của giá, từ thời điểm giá Open được xác định. Điều này cho phép tạo ra một "Chén Thánh Kiểm tra", cho thấy một biểu đồ tăng trưởng đẹp mắt của số dư kiểm tra.
Một ví dụ về Chén Thánh như vậy được trình bày trong CodeBase - Grr-al.
Hình ảnh cho thấy một biểu đồ rất hấp dẫn của việc kiểm tra EA này. Nó được thu thập như thế nào? Chúng ta biết 4 giá cho một thanh phút, và chúng ta cũng biết rằng giá đầu tiên là giá Open, và giá cuối cùng là giá Close. Chúng ta có giá High và Low giữa chúng, và thứ tự xuất hiện của chúng không được biết, nhưng chúng ta biết rằng giá High lớn hơn hoặc bằng giá Open (và giá Low nhỏ hơn hoặc bằng giá Open).
Chỉ cần xác định thời điểm nhận giá Open, sau đó phân tích tick tiếp theo để xác định giá hiện tại là gì - High hay Low. Nếu giá thấp hơn giá Open, thì chúng ta có giá Low và mua tại tick này, tick tiếp theo sẽ tương ứng với giá High, tại đó chúng ta sẽ đóng lệnh mua và mở lệnh bán. Tick tiếp theo là tick cuối cùng, đây là giá Close, và chúng ta đóng lệnh bán tại đó.
Nếu sau giá Open, chúng ta nhận được tick với giá lớn hơn giá mở, thì thứ tự các giao dịch sẽ đảo ngược. Xử lý thanh phút trong chế độ "gian lận" này và chờ thanh tiếp theo.
Khi kiểm tra EA như vậy trên lịch sử, mọi thứ diễn ra suôn sẻ, nhưng một khi chúng ta khởi chạy nó trực tuyến, sự thật bắt đầu lộ diện - đường số dư vẫn ổn định, nhưng hướng xuống dưới. Để phơi bày thủ thuật này, chúng ta chỉ cần chạy EA ở chế độ "Mọi tick".
Lưu ý: Nếu kết quả kiểm tra của EA trong các chế độ kiểm tra thô ("OHLC 1 phút" và "Chỉ giá mở") có vẻ quá tốt, hãy đảm bảo kiểm tra nó ở chế độ "Mọi tick".
"Chỉ giá mở"
Trong chế độ này, các tick được tạo dựa trên giá OHLC của khung thời gian được chọn để kiểm tra. Hàm OnTick()
của Expert Advisor chỉ chạy vào đầu thanh tại giá Open. Do tính năng này, các mức dừng và lệnh chờ có thể kích hoạt ở giá khác với giá được chỉ định (đặc biệt khi kiểm tra trên các khung thời gian cao hơn). Thay vào đó, chúng ta có cơ hội chạy nhanh một bài kiểm tra đánh giá của Expert Advisor.
Các kỳ W1 và MN1 là ngoại lệ trong chế độ tạo tick "Chỉ giá mở": đối với các khung thời gian này, tick được tạo cho giá OHLC của mỗi ngày, không phải giá OHLC của tuần hoặc tháng.
Giả sử chúng ta kiểm tra một Expert Advisor trên EURUSD H1 ở chế độ "Chỉ giá mở". Trong trường hợp này, tổng số tick (điểm kiểm soát) sẽ không vượt quá 4 * số lượng thanh một giờ trong khoảng thời gian được kiểm tra. Nhưng trình xử lý OnTick() chỉ được gọi khi mở thanh một giờ
. Các kiểm tra cần thiết cho việc kiểm tra chính xác diễn ra trên các tick còn lại (bị "ẩn" khỏi EA).
- Tính toán yêu cầu ký quỹ;
- Kích hoạt các mức Stop Loss và Take Profit;
- Kích hoạt các lệnh chờ;
- Xóa các lệnh chờ đã hết hạn.
Nếu không có vị thế mở hoặc lệnh chờ, chúng ta không cần thực hiện các kiểm tra này trên các tick ẩn, và tốc độ tăng có thể khá đáng kể. Chế độ "Chỉ giá mở" này rất phù hợp để kiểm tra các chiến lược chỉ xử lý giao dịch khi mở thanh và không sử dụng lệnh chờ, cũng như lệnh StopLoss và TakeProfit. Đối với nhóm chiến lược này, độ chính xác cần thiết của việc kiểm tra được duy trì.
Hãy sử dụng Expert Advisor Moving Average từ gói tiêu chuẩn làm ví dụ về EA có thể được kiểm tra ở bất kỳ chế độ nào. Logic của EA này được xây dựng sao cho tất cả các quyết định được đưa ra khi mở thanh, và các giao dịch được thực hiện ngay lập tức, mà không sử dụng lệnh chờ.
Chạy kiểm tra EA trên EURUSD H1 trong khoảng từ 2010.09.01 đến 2010.12.31, và so sánh các biểu đồ. Hình ảnh cho thấy biểu đồ số dư từ báo cáo kiểm tra cho cả ba chế độ.
Như bạn thấy, các biểu đồ trên các chế độ kiểm tra khác nhau là hoàn toàn giống nhau đối với EA Moving Average từ gói tiêu chuẩn.
Có một số giới hạn trong chế độ "Chỉ giá mở":
- Bạn không thể sử dụng chế độ thực thi trễ ngẫu nhiên.
- Trong Expert Advisor được kiểm tra, bạn không thể truy cập dữ liệu của khung thời gian thấp hơn khung được sử dụng để kiểm tra/tối ưu hóa. Ví dụ, nếu bạn chạy kiểm tra/tối ưu hóa trên kỳ H1, bạn có thể truy cập dữ liệu của H2, H3, H4, v.v., nhưng không phải M30, M20, M10, v.v. Ngoài ra, các khung thời gian cao hơn được truy cập phải là bội số của khung thời gian kiểm tra. Ví dụ, nếu bạn chạy kiểm tra trên M20, bạn không thể truy cập dữ liệu của M30, nhưng có thể truy cập H1. Những giới hạn này liên quan đến việc không thể lấy dữ liệu của các khung thời gian thấp hơn hoặc không phải bội số từ các thanh được tạo trong quá trình kiểm tra/tối ưu hóa.
- Giới hạn truy cập dữ liệu của các khung thời gian khác cũng áp dụng cho các biểu tượng khác mà dữ liệu của chúng được EA sử dụng. Trong trường hợp này, giới hạn cho mỗi biểu tượng phụ thuộc vào khung thời gian đầu tiên được truy cập trong quá trình kiểm tra/tối ưu hóa. Giả sử, trong khi kiểm tra trên EURUSD H1, một EA truy cập dữ liệu của GBPUSD M20. Trong trường hợp này, EA sẽ có thể tiếp tục sử dụng dữ liệu của EURUSD H1, H2, v.v., cũng như GBPUSD M20, H1, H2, v.v.
Lưu ý: Chế độ "Chỉ giá mở" có thời gian kiểm tra nhanh nhất, nhưng không phù hợp với tất cả các chiến lược giao dịch. Chọn chế độ kiểm tra mong muốn dựa trên đặc điểm của hệ thống giao dịch.
Để kết thúc phần về chế độ tạo tick, hãy xem xét một so sánh trực quan của các chế độ tạo tick khác nhau cho EURUSD, cho hai thanh M15 trong khoảng từ 2011.01.11 21:00:00 - 2011.01.11 21:30:00.
Các tick đã được lưu vào các tệp khác nhau bằng EA WriteTicksFromTester.mq5 và phần cuối của tên tệp được chỉ định trong các tham số đầu vào filenameEveryTick
, filenameOHLC
và filenameOpenPrice
.
Để lấy ba tệp với ba chuỗi tick (cho từng chế độ "Mọi tick", "OHLC 1 phút" và "Chỉ giá mở"), EA đã được khởi chạy ba lần ở các chế độ tương ứng, trong các lần chạy đơn. Sau đó, dữ liệu từ ba tệp này đã được hiển thị trên biểu đồ bằng chỉ báo TicksFromTester.mq5. Mã chỉ báo được đính kèm với bài viết này.
Theo mặc định, tất cả hoạt động tệp trong ngôn ngữ MQL5 được thực hiện trong "hộp cát tệp", và trong quá trình kiểm tra, EA chỉ có quyền truy cập vào "hộp cát tệp" của riêng nó. Để chỉ báo và EA hoạt động với các tệp từ một thư mục trong quá trình kiểm tra, chúng tôi đã sử dụng cờ FILE_COMMON. Ví dụ mã từ EA:
//--- mở tệp
file=FileOpen(filename,FILE_WRITE|FILE_CSV|FILE_COMMON,";");
//--- kiểm tra tay cầm tệp
if(file==INVALID_HANDLE)
{
PrintFormat("Lỗi khi mở tệp %s để ghi. Mã lỗi=%d",filename,GetLastError());
return;
}
else
{
PrintFormat("Tệp sẽ được tạo trong thư mục %s",TerminalInfoString(TERMINAL_COMMONDATA_PATH));
}
2
3
4
5
6
7
8
9
10
11
12
Để đọc dữ liệu trong chỉ báo, chúng tôi cũng sử dụng cờ FILE_COMMON. Điều này cho phép chúng tôi tránh việc chuyển thủ công các tệp cần thiết từ thư mục này sang thư mục khác.
//--- mở tệp
int file=FileOpen(fname,FILE_READ|FILE_CSV|FILE_COMMON,";");
//--- kiểm tra tay cầm tệp
if(file==INVALID_HANDLE)
{
PrintFormat("Lỗi khi mở tệp %s để đọc. Mã lỗi=%d",fname,GetLastError());
return;
}
else
{
PrintFormat("Tệp sẽ được mở từ %s",TerminalInfoString(TERMINAL_COMMONDATA_PATH));
}
2
3
4
5
6
7
8
9
10
11
12
Mô phỏng spread
Khoảng cách giá giữa giá Bid và giá Ask được gọi là spread. Trong quá trình kiểm tra, spread không được mô phỏng mà được lấy từ dữ liệu lịch sử. Nếu spread nhỏ hơn hoặc bằng không trong dữ liệu lịch sử, thì spread cuối cùng được biết (tại thời điểm tạo) sẽ được sử dụng bởi tác nhân kiểm tra.
Trong Strategy Tester, spread luôn được coi là biến động. Nghĩa là, SymbolInfoInteger
(symbol, SYMBOL_SPREAD_FLOAT) luôn trả về true
.
Ngoài ra, dữ liệu lịch sử chứa giá trị tick và khối lượng giao dịch. Để lưu trữ và truy xuất dữ liệu, chúng tôi sử dụng một cấu trúc đặc biệt MqlRates
:
struct MqlRates
{
datetime time; // Thời gian bắt đầu kỳ
double open; // Giá mở
double high; // Giá cao nhất của kỳ
double low; // Giá thấp nhất của kỳ
double close; // Giá đóng
long tick_volume; // Khối lượng tick
int spread; // Spread
long real_volume; // Khối lượng giao dịch
};
2
3
4
5
6
7
8
9
10
11
Sử dụng tick thực trong quá trình kiểm tra
Kiểm tra và tối ưu hóa trên tick thực gần với điều kiện thực tế nhất có thể. Thay vì sử dụng các tick được tạo dựa trên dữ liệu phút, có thể sử dụng các tick thực được tích lũy bởi một nhà môi giới. Đây là các tick từ các sàn giao dịch và nhà cung cấp thanh khoản.
Để đảm bảo độ chính xác cao nhất trong kiểm tra, các thanh phút cũng được sử dụng trong chế độ tick thực. Các thanh này được áp dụng để kiểm tra và sửa dữ liệu tick. Điều này cũng giúp tránh sự khác biệt giữa biểu đồ trong tester và terminal client.
Tester so sánh dữ liệu tick với các tham số của thanh phút: một tick không được vượt quá mức High/Low của thanh, đồng thời các tick đầu tiên và cuối cùng phải trùng với giá Open/Close của thanh. Khối lượng cũng được so sánh. Nếu phát hiện không khớp, tất cả các tick tương ứng với thanh phút này sẽ bị loại bỏ. Thay vào đó, các tick được tạo sẽ được sử dụng (giống như trong chế độ "Mọi tick").
Nếu lịch sử của một biểu tượng có thanh phút mà không có dữ liệu tick cho nó, tester sẽ tạo tick trong chế độ "Mọi tick". Điều này cho phép vẽ biểu đồ chính xác trong tester trong trường hợp dữ liệu tick của nhà môi giới không đủ.
Nếu lịch sử của một biểu tượng không có thanh phút nhưng có dữ liệu tick phù hợp cho phút đó, dữ liệu này có thể được sử dụng trong tester. Ví dụ, các cặp biểu tượng sàn giao dịch được hình thành bằng giá Last. Nếu chỉ có các tick với giá Bid/Ask mà không có giá Last đến từ máy chủ, thanh sẽ không được tạo. Tester sử dụng dữ liệu tick này vì chúng không mâu thuẫn với dữ liệu phút.
Dữ liệu tick có thể không trùng với các thanh phút vì nhiều lý do, ví dụ như mất kết nối hoặc các lỗi khác khi truyền dữ liệu từ nguồn đến terminal client. Dữ liệu phút được coi là đáng tin cậy hơn trong quá trình kiểm tra.
Hãy lưu ý các đặc điểm sau khi kiểm tra trên tick thực:
- Khi khởi chạy kiểm tra, dữ liệu phút của một biểu tượng được đồng bộ cùng với dữ liệu tick.
- Các tick được lưu trữ trong bộ đệm biểu tượng của Strategy Tester. Kích thước bộ đệm không vượt quá 128 000 tick. Khi có tick mới đến, dữ liệu cũ nhất sẽ bị xóa khỏi bộ đệm. Tuy nhiên, hàm
CopyTicks
cho phép nhận các tick ngoài bộ đệm (chỉ khi kiểm tra trên tick thực). Trong trường hợp đó, dữ liệu được yêu cầu từ cơ sở dữ liệu tick của tester, hoàn toàn tương tự với cơ sở dữ liệu terminal client tương ứng. Không có sửa chữa thanh phút nào được thực hiện cho cơ sở này. Do đó, các tick ở đó có thể khác với các tick được lưu trong bộ đệm.
Biến toàn cục của Terminal Client
Trong quá trình kiểm tra, biến toàn cục của terminal client cũng được mô phỏng, nhưng chúng không liên quan đến biến toàn cục hiện tại của terminal, có thể được xem trong terminal bằng phím F3. Điều này có nghĩa là tất cả các thao tác với biến toàn cục của terminal, trong quá trình kiểm tra, diễn ra bên ngoài terminal client (trong tác nhân kiểm tra).
Tính toán chỉ báo trong quá trình kiểm tra
Trong chế độ thời gian thực, giá trị chỉ báo được tính toán tại mỗi tick.
Trong Strategy Tester, chỉ báo chỉ được tính toán khi được truy cập dữ liệu, tức là khi giá trị bộ đệm chỉ báo được yêu cầu. Ngoại lệ duy nhất là chỉ báo tùy chỉnh với #property tester_everytick_calculate
được chỉ định. Trong trường hợp này, việc tính toán lại được thực hiện trên mỗi tick.
Trong chế độ kiểm tra trực quan, tất cả các chỉ báo được tính toán lại vô điều kiện khi có tick mới đến để hiển thị chính xác trên biểu đồ kiểm tra trực quan.
Chỉ báo được tính toán một lần cho mỗi tick. Tất cả các yêu cầu dữ liệu chỉ báo tiếp theo không dẫn đến việc tính toán lại cho đến khi có tick mới đến. Do đó, nếu bộ đếm thời gian được kích hoạt trong EA qua hàm EventSetTimer()
, dữ liệu chỉ báo được yêu cầu từ tick cuối cùng trước mỗi lần gọi trình xử lý OnTimer()
. Nếu chỉ báo chưa được tính toán trên tick cuối cùng, việc tính toán giá trị chỉ báo sẽ được khởi động. Nếu dữ liệu đã được chuẩn bị, nó sẽ được cung cấp mà không cần tính toán lại.
Do đó, tất cả các tính toán chỉ báo được thực hiện theo cách tiết kiệm tài nguyên nhất — nếu chỉ báo đã được tính toán tại một tick nhất định, dữ liệu của nó sẽ được cung cấp như hiện tại
. Không có tính toán lại nào được khởi động.
Tải lịch sử trong quá trình kiểm tra
Lịch sử của một biểu tượng cần kiểm tra được terminal đồng bộ và tải từ máy chủ giao dịch trước khi bắt đầu quá trình kiểm tra. Lần đầu tiên, terminal tải toàn bộ lịch sử có sẵn của một biểu tượng để không phải yêu cầu lại sau này. Sau đó, chỉ dữ liệu mới được tải thêm.
Tác nhân kiểm tra nhận lịch sử của biểu tượng cần kiểm tra từ terminal client ngay sau khi bắt đầu kiểm tra. Nếu dữ liệu của các công cụ khác được sử dụng trong quá trình kiểm tra (ví dụ, đó là một Expert Advisor đa tiền tệ), tác nhân kiểm tra yêu cầu lịch sử cần thiết từ terminal client trong lần gọi đầu tiên đến dữ liệu đó. Nếu dữ liệu lịch sử có sẵn trong terminal, chúng sẽ được truyền ngay lập tức cho tác nhân kiểm tra. Nếu dữ liệu không có sẵn, terminal yêu cầu và tải chúng từ máy chủ, sau đó truyền cho tác nhân kiểm tra.
Dữ liệu của các công cụ bổ sung cũng cần thiết để tính toán tỷ giá chéo cho các hoạt động giao dịch. Ví dụ, khi kiểm tra một chiến lược trên EURCHF với tiền tệ ký quỹ là USD, trước khi xử lý hoạt động giao dịch đầu tiên, tác nhân kiểm tra yêu cầu dữ liệu lịch sử của EURUSD và USDCHF từ terminal client, mặc dù chiến lược không chứa lệnh gọi trực tiếp đến các biểu tượng này.
Trước khi kiểm tra một chiến lược đa tiền tệ, nên tải tất cả dữ liệu lịch sử cần thiết vào terminal client. Điều này sẽ giúp tránh sự chậm trễ trong kiểm tra/tối ưu hóa liên quan đến việc tải dữ liệu cần thiết. Bạn có thể tải lịch sử, ví dụ, bằng cách mở các biểu đồ tương ứng và cuộn chúng đến đầu lịch sử. Một ví dụ về việc tải lịch sử bắt buộc vào terminal có sẵn trong phần Tổ chức truy cập dữ liệu của Tài liệu tham khảo MQL5.
Các tác nhân kiểm tra, đến lượt mình, nhận lịch sử từ terminal dưới dạng đóng gói. Trong lần kiểm tra tiếp theo, tester không tải lại lịch sử từ terminal, vì dữ liệu cần thiết đã có sẵn từ lần chạy trước của tester.
- Terminal chỉ tải lịch sử từ máy chủ giao dịch một lần, lần đầu tiên tác nhân yêu cầu lịch sử của một biểu tượng cần kiểm tra từ terminal. Lịch sử được tải dưới dạng đóng gói để giảm lưu lượng.
- Tick không được gửi qua mạng, chúng được tạo trên các tác nhân kiểm tra.
Kiểm tra đa tiền tệ
Strategy Tester cho phép thực hiện kiểm tra các chiến lược giao dịch trên nhiều biểu tượng. Các EA như vậy thường được gọi là Expert Advisor đa tiền tệ, vì ban đầu, trong các nền tảng trước đây, kiểm tra chỉ được thực hiện cho một biểu tượng duy nhất. Trong Strategy Tester của terminal MetaTrader 5, chúng ta có thể mô phỏng giao dịch cho tất cả các biểu tượng có sẵn.
Tester tải lịch sử của các biểu tượng được sử dụng từ terminal client
(không phải từ máy chủ giao dịch!) tự động trong lần gọi đầu tiên đến dữ liệu biểu tượng.
Tác nhân kiểm tra chỉ tải xuống lịch sử còn thiếu, với một khoảng dự phòng nhỏ để cung cấp dữ liệu cần thiết trên lịch sử, cho việc tính toán các chỉ báo tại thời điểm bắt đầu kiểm tra. Đối với các khung thời gian D1 trở xuống, khối lượng tối thiểu của lịch sử được tải xuống là một năm.
Do đó, nếu chúng ta chạy kiểm tra trên khoảng thời gian 2010.11.01-2010.12.01 (kiểm tra cho khoảng thời gian một tháng) với kỳ M15 (mỗi thanh bằng 15 phút), thì terminal sẽ được yêu cầu lịch sử cho công cụ trong suốt năm 2010. Đối với khung thời gian hàng tuần, chúng ta sẽ yêu cầu lịch sử 100 thanh, khoảng hai năm (một năm có 52 tuần). Đối với kiểm tra trên khung thời gian hàng tháng, tác nhân sẽ yêu cầu lịch sử 8 năm (12 tháng x 8 năm = 96 tháng).
Nếu không có đủ thanh cần thiết, ngày bắt đầu kiểm tra sẽ tự động được dịch chuyển
từ quá khứ đến hiện tại để cung cấp dự phòng thanh cần thiết trước khi kiểm tra.
Trong quá trình kiểm tra, "Market Watch" cũng được mô phỏng, từ đó có thể lấy thông tin về các biểu tượng.
Theo mặc định, tại thời điểm bắt đầu kiểm tra, chỉ có một biểu tượng trong "Market Watch" của Strategy Tester - biểu tượng mà kiểm tra đang chạy trên đó. Tất cả các biểu tượng cần thiết được kết nối với "Market Watch" của Strategy Tester (không phải terminal!) tự động khi được tham chiếu đến.
Trước khi bắt đầu kiểm tra một Expert Advisor đa tiền tệ, cần chọn các biểu tượng cần thiết cho kiểm tra trong "Market Watch" của terminal và tải dữ liệu cần thiết. Trong lần gọi đầu tiên đến một biểu tượng "ngoài", lịch sử của nó sẽ tự động được đồng bộ giữa tác nhân kiểm tra và terminal client. Biểu tượng "ngoài" là biểu tượng khác với biểu tượng mà kiểm tra đang chạy trên đó.
Việc tham chiếu đến dữ liệu của một biểu tượng "khác" xảy ra trong các trường hợp sau:
- Khi sử dụng hàm chỉ báo kỹ thuật và
IndicatorCreate()
trên biểu tượng/khung thời gian; - Yêu cầu dữ liệu "Market Watch" cho biểu tượng khác:
- Yêu cầu chuỗi thời gian cho một biểu tượng/khung thời gian bằng cách sử dụng các hàm sau:
Tại thời điểm lần gọi đầu tiên đến một biểu tượng "khác", quá trình kiểm tra bị dừng lại và lịch sử được tải xuống cho biểu tượng/khung thời gian, từ terminal đến tác nhân kiểm tra. Đồng thời, chuỗi tick cho biểu tượng này được tạo ra.
Một chuỗi tick riêng lẻ được tạo cho mỗi biểu tượng, theo chế độ tạo tick đã chọn. Bạn cũng có thể yêu cầu lịch sử một cách rõ ràng cho các biểu tượng mong muốn bằng cách gọi SymbolSelect()
trong trình xử lý OnInit()
- việc tải lịch sử sẽ được thực hiện ngay lập tức trước khi kiểm tra Expert Advisor.
Do đó, không cần nỗ lực thêm để thực hiện kiểm tra đa tiền tệ trong terminal client MetaTrader 5. Chỉ cần mở các biểu đồ của các biểu tượng phù hợp trong terminal client. Lịch sử sẽ tự động được tải lên từ máy chủ giao dịch cho tất cả các biểu tượng cần thiết, với điều kiện nó chứa dữ liệu này.
Mô phỏng thời gian trong Strategy Tester
Trong quá trình kiểm tra, thời gian cục bộ TimeLocal()
luôn bằng thời gian máy chủ TimeTradeServer()
. Đến lượt nó, thời gian máy chủ luôn bằng thời gian tương ứng với thời gian GMT - TimeGMT()
. Bằng cách này, tất cả các hàm này hiển thị cùng một thời gian trong quá trình kiểm tra.
Việc không có sự khác biệt giữa GMT, thời gian cục bộ và thời gian máy chủ trong Strategy Tester được thực hiện cố ý trong trường hợp không có kết nối với máy chủ. Kết quả kiểm tra phải luôn giống nhau, bất kể có kết nối hay không. Thông tin về thời gian máy chủ không được lưu trữ cục bộ và được lấy từ máy chủ.
Đối tượng đồ họa trong kiểm tra
Trong quá trình kiểm tra/tối ưu hóa, các đối tượng đồ họa không được vẽ. Do đó, khi tham chiếu đến các thuộc tính của một đối tượng được tạo trong quá trình kiểm tra/tối ưu hóa, Expert Advisor sẽ nhận được giá trị bằng không.
Giới hạn này không áp dụng cho kiểm tra ở chế độ trực quan.
Hàm OnTimer()
trong Strategy Tester
MQL5 cung cấp cơ hội xử lý các sự kiện bộ đếm thời gian. Việc gọi trình xử lý OnTimer()
được thực hiện bất kể chế độ kiểm tra. Điều này có nghĩa là nếu kiểm tra chạy ở chế độ "Chỉ giá mở" cho kỳ H4, và EA có bộ đếm thời gian được đặt để gọi mỗi giây, thì khi mở mỗi thanh H4, trình xử lý OnTick()
sẽ được gọi một lần, và trình xử lý OnTimer()
sẽ được gọi 14400 lần (3600 giây * 4 giờ). Mức độ tăng thời gian kiểm tra của EA phụ thuộc vào logic của EA.
Để kiểm tra sự phụ thuộc của thời gian kiểm tra vào tần suất bộ đếm thời gian đã cho, chúng tôi đã tạo một EA đơn giản không có bất kỳ hoạt động giao dịch nào.
//--- tham số đầu vào
input int timer=1; // giá trị bộ đếm thời gian, giây
input bool timer_switch_on=true; // bật bộ đếm thời gian
//+------------------------------------------------------------------+
//| Hàm khởi tạo Expert |
//+------------------------------------------------------------------+
int OnInit()
{
//--- chạy bộ đếm thời gian nếu timer_switch_on==true
if(timer_switch_on)
{
EventSetTimer(timer);
}
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Hàm hủy khởi tạo Expert |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- dừng bộ đếm thời gian
EventKillTimer();
}
//+------------------------------------------------------------------+
//| Hàm bộ đếm thời gian |
//+------------------------------------------------------------------+
void OnTimer()
{
//---
// không thực hiện hành động, phần thân của trình xử lý trống
}
//+------------------------------------------------------------------+
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Đo lường thời gian kiểm tra được thực hiện ở các giá trị khác nhau của tham số bộ đếm thời gian (tần suất của sự kiện Timer). Trên dữ liệu thu được, chúng tôi vẽ thời gian kiểm tra như một hàm của chu kỳ Timer.
Có thể thấy rõ rằng tham số timer càng nhỏ, trong quá trình khởi tạo hàm EventSetTimer
(Timer), chu kỳ (Period) giữa các lần gọi trình xử lý OnTimer()
càng nhỏ, và thời gian kiểm tra T càng lớn, trong cùng các điều kiện khác.
Hàm Sleep()
trong Strategy Tester
Hàm Sleep()
cho phép EA hoặc script tạm dừng thực thi chương trình mql5 trong một khoảng thời gian, khi làm việc trên biểu đồ. Điều này có thể hữu ích khi yêu cầu dữ liệu chưa sẵn sàng tại thời điểm yêu cầu và bạn cần đợi cho đến khi nó sẵn sàng. Một ví dụ chi tiết về việc sử dụng hàm Sleep()
có thể được tìm thấy trong phần Tổ chức truy cập dữ liệu.
Quá trình kiểm tra không bị chậm trễ bởi các lệnh gọi Sleep()
. Khi bạn gọi Sleep()
, các tick được tạo sẽ được "phát" trong khoảng thời gian trễ được chỉ định, điều này có thể dẫn đến việc kích hoạt các lệnh chờ, dừng lỗ, v.v. Sau khi gọi Sleep()
, thời gian mô phỏng trong Strategy Tester tăng lên một khoảng thời gian, được chỉ định trong tham số của hàm Sleep
.
Nếu kết quả của việc thực thi hàm Sleep()
, thời gian hiện tại trong Strategy Tester vượt quá khoảng thời gian kiểm tra, thì bạn sẽ nhận được lỗi "Phát hiện vòng lặp Sleep vô hạn trong khi kiểm tra". Nếu bạn nhận được lỗi này, kết quả kiểm tra không bị từ chối, tất cả các tính toán được thực hiện đầy đủ (số lượng giao dịch, độ sụt giảm, v.v.), và kết quả của lần kiểm tra này được truyền đến terminal.
Hàm Sleep()
sẽ không hoạt động trong OnDeinit()
, vì sau khi nó được gọi, thời gian kiểm tra sẽ chắc chắn vượt quá phạm vi khoảng thời gian kiểm tra.
Sử dụng Strategy Tester cho các bài toán tối ưu hóa trong tính toán toán học
Tester trong terminal MetaTrader 5 có thể được sử dụng không chỉ để kiểm tra các chiến lược giao dịch mà còn cho các tính toán toán học. Để sử dụng nó, cần chọn chế độ "Tính toán toán học":
Trong trường hợp này, chỉ ba hàm sẽ được gọi: OnInit()
, OnTester()
, OnDeinit()
. Trong chế độ "Tính toán toán học", Strategy Tester không tạo bất kỳ tick nào và không tải lịch sử.
Strategy Tester hoạt động ở chế độ "Tính toán toán học" cũng nếu bạn chỉ định ngày bắt đầu lớn hơn ngày kết thúc.
Khi sử dụng tester để giải quyết các bài toán toán học, việc tải lịch sử và tạo tick không xảy ra.
Một bài toán toán học điển hình để giải trong Strategy Tester MetaTrader 5 - tìm cực trị của một hàm với nhiều biến.
Để giải quyết nó, chúng ta cần:
- Việc tính toán giá trị hàm nên được đặt trong hàm
OnTester()
; - Các tham số của hàm phải được định nghĩa là biến đầu vào của Expert Advisor;
Biên dịch EA, mở cửa sổ "Strategy Tester". Trong tab "Tham số đầu vào", chọn các biến đầu vào cần thiết, và xác định tập hợp giá trị tham số bằng cách chỉ định giá trị bắt đầu, kết thúc và bước cho mỗi biến của hàm.
Chọn loại tối ưu hóa - "Thuật toán đầy đủ chậm" (tìm kiếm toàn bộ không gian tham số) hoặc "Thuật toán dựa trên di truyền nhanh". Để tìm kiếm đơn giản cực trị của hàm, tốt hơn nên chọn tối ưu hóa nhanh, nhưng nếu bạn muốn tính toán giá trị cho toàn bộ tập hợp biến, thì tốt nhất nên sử dụng tối ưu hóa chậm.
Chọn chế độ "Tính toán toán học" và sử dụng nút "Bắt đầu", chạy quy trình tối ưu hóa. Lưu ý rằng trong quá trình tối ưu hóa, Strategy Tester sẽ tìm kiếm giá trị tối đa của hàm OnTester
. Để tìm cực tiểu cục bộ, trả về nghịch đảo của giá trị hàm đã tính từ hàm OnTester
:
return(1/function_value);
Cần kiểm tra rằng function_value
không bằng không, vì nếu không, chúng ta có thể gặp lỗi nghiêm trọng chia cho không.
Có một cách khác, thuận tiện hơn và không làm sai lệch kết quả tối ưu hóa, được đề xuất bởi độc giả của bài viết này:
return(-function_value);
Tùy chọn này không yêu cầu kiểm tra function_value
có bằng không hay không, và bề mặt kết quả tối ưu hóa trong biểu diễn 3D có cùng hình dạng. Sự khác biệt duy nhất là nó được phản chiếu so với bản gốc.
Ví dụ, chúng tôi cung cấp hàm sink()
:
Mã của EA để tìm cực trị của hàm này được đặt trong OnTester()
:
//+------------------------------------------------------------------+
//| Sink.mq5 |
//| Copyright 2011, MetaQuotes Software Corp. |
//| https://www.mql5.com|
//+------------------------------------------------------------------+
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
//--- tham số đầu vào
input double x=-3.0; // start=-3, step=0.05, stop=3
input double y=-3.0; // start=-3, step=0.05, stop=3
//+------------------------------------------------------------------+
//| Hàm Tester |
//+------------------------------------------------------------------+
double OnTester()
{
//---
double sink=MathSin(x*x+y*y);
//---
return(sink);
}
//+------------------------------------------------------------------+
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Thực hiện tối ưu hóa và xem kết quả tối ưu hóa dưới dạng biểu đồ 2D.
Giá trị càng tốt cho một cặp tham số (x, y) nhất định, màu sắc càng đậm. Như kỳ vọng từ dạng công thức của sink()
, các giá trị của nó tạo thành các vòng tròn đồng tâm với tâm tại (0,0). Có thể thấy trong biểu đồ 3D, hàm sink()
không có cực trị toàn cục duy nhất:
Đồng bộ hóa thanh trong chế độ "Chỉ giá mở"
Tester trong terminal client MetaTrader 5 cho phép chúng ta kiểm tra các EA "đa tiền tệ". Một EA đa tiền tệ là EA giao dịch trên hai hoặc nhiều biểu tượng.
Việc kiểm tra các chiến lược giao dịch trên nhiều biểu tượng đặt ra một vài yêu cầu kỹ thuật bổ sung đối với tester:
- Tạo tick cho các biểu tượng này;
- Tính toán giá trị chỉ báo cho các biểu tượng này;
- Tính toán yêu cầu ký quỹ cho các biểu tượng này;
- Đồng bộ hóa các chuỗi tick được tạo cho tất cả các biểu tượng giao dịch.
Strategy Tester tạo và phát chuỗi tick cho mỗi công cụ theo chế độ giao dịch đã chọn. Đồng thời, một thanh mới cho mỗi biểu tượng được mở, bất kể thanh đó mở như thế nào trên một biểu tượng khác. Điều này có nghĩa là khi kiểm tra một EA đa tiền tệ, có thể xảy ra tình huống (và thường xuyên xảy ra), khi một công cụ đã mở thanh mới, còn công cụ khác thì chưa. Do đó, trong kiểm tra, mọi thứ diễn ra giống như trong thực tế.
Mô phỏng lịch sử chân thực này trong tester không gây ra bất kỳ vấn đề nào miễn là các chế độ kiểm tra "Mọi tick" và "OHLC 1 phút" được sử dụng. Đối với các chế độ này, đủ tick được tạo cho một cây nến để có thể đợi cho đến khi đồng bộ hóa các thanh từ các biểu tượng khác nhau diễn ra. Nhưng làm thế nào để kiểm tra các chiến lược đa tiền tệ trong chế độ "Chỉ giá mở", nếu việc đồng bộ hóa thanh trên các công cụ giao dịch là bắt buộc? Trong chế độ này, EA chỉ được gọi trên một tick, tương ứng với thời điểm mở thanh.
Chúng tôi sẽ minh họa điều này bằng một ví dụ: chúng ta đang kiểm tra một EA trên EURUSD, và một cây nến H1 mới đã được mở trên EURUSD. Chúng ta có thể dễ dàng nhận ra sự thật này - trong khi kiểm tra ở chế độ "Chỉ giá mở", sự kiện NewTick
tương ứng với thời điểm mở thanh trên kỳ được kiểm tra. Nhưng không có gì đảm bảo rằng cây nến mới đã được mở trên biểu tượng USDJPY, được sử dụng trong EA.
Trong trường hợp bình thường, chỉ cần hoàn thành công việc của hàm OnTick()
và kiểm tra sự xuất hiện của thanh mới trên USDJPY tại tick tiếp theo là đủ. Nhưng khi kiểm tra ở chế độ "Chỉ giá mở", sẽ không có tick nào khác, và có vẻ như chế độ này không phù hợp để kiểm tra các EA đa tiền tệ. Nhưng không phải vậy - đừng quên rằng tester trong MetaTrader 5 hoạt động giống như trong đời thực. Bạn có thể đợi cho đến khi một thanh mới được mở trên các biểu tượng khác bằng cách sử dụng hàm Sleep()
!
Mã của EA Synchronize_Bars_Use_Sleep.mq5
, cho thấy một ví dụ về đồng bộ hóa thanh trong chế độ "Chỉ giá mở":
//+------------------------------------------------------------------+
//| Synchronize_Bars_Use_Sleep.mq5 |
//| Copyright 2011, MetaQuotes Software Corp. |
//| https://www.mql5.com|
//+------------------------------------------------------------------+
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
//--- tham số đầu vào
input string other_symbol="USDJPY";
//+------------------------------------------------------------------+
//| Hàm khởi tạo Expert |
//+------------------------------------------------------------------+
int OnInit()
{
//--- kiểm tra biểu tượng
if(_Symbol==other_symbol)
{
PrintFormat("Bạn phải chỉ định biểu tượng khác trong tham số đầu vào hoặc chọn biểu tượng khác trong Strategy Tester!");
//--- buộc dừng kiểm tra
return(INIT_PARAMETERS_INCORRECT);
}
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Hàm tick Expert |
//+------------------------------------------------------------------+
void OnTick()
{
//--- biến tĩnh, dùng để lưu thời gian thanh cuối cùng
static datetime last_bar_time=0;
//--- cờ đồng bộ
static bool synchonized=false;
//--- nếu biến tĩnh chưa được khởi tạo
if(last_bar_time==0)
{
//--- lần gọi đầu tiên, lưu thời gian thanh và thoát
last_bar_time=(datetime)SeriesInfoInteger(_Symbol,Period(),SERIES_LASTBAR_DATE);
PrintFormat("Biến last_bar_time được khởi tạo với giá trị %s",TimeToString(last_bar_time));
}
//--- lấy thời gian mở của thanh cuối cùng của biểu tượng biểu đồ
datetime curr_time=(datetime)SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE);
//--- nếu thời gian không bằng nhau
if(curr_time!=last_bar_time)
{
//--- lưu thời gian mở thanh vào biến tĩnh
last_bar_time=curr_time;
//--- chưa đồng bộ
synchonized=false;
//--- in thông báo
PrintFormat("Một thanh mới đã xuất hiện trên biểu tượng %s lúc %s",_Symbol,TimeToString(TimeCurrent()));
}
//--- thời gian mở của thanh của biểu tượng khác
datetime other_time;
//--- lặp cho đến khi thời gian mở của biểu tượng khác bằng curr_time
while(!(curr_time==(other_time=(datetime)SeriesInfoInteger(other_symbol,Period(),SERIES_LASTBAR_DATE)) && !synchonized))
{
PrintFormat("Đợi 5 giây..");
//--- đợi 5 giây và gọi SeriesInfoInteger(other_symbol,Period(),SERIES_LASTBAR_DATE)
Sleep(5000);
}
//--- các thanh đã được đồng bộ
synchonized=true;
PrintFormat("Thời gian mở thanh của biểu tượng biểu đồ %s: là %s",_Symbol,TimeToString(last_bar_time));
PrintFormat("Thời gian mở thanh của biểu tượng %s: là %s",other_symbol,TimeToString(other_time));
//--- TimeCurrent() không hữu ích, sử dụng TimeTradeServer()
Print("Các thanh đã được đồng bộ lúc ",TimeToString(TimeTradeServer(),TIME_SECONDS));
}
//+------------------------------------------------------------------+
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
Lưu ý dòng cuối cùng trong EA, hiển thị thời gian hiện tại khi sự đồng bộ được thiết lập:
Print("Các thanh đã được đồng bộ lúc ",TimeToString(TimeTradeServer(),TIME_SECONDS));
Để hiển thị thời gian hiện tại, chúng tôi đã sử dụng hàm TimeTradeServer()
thay vì TimeCurrent()
. Hàm TimeCurrent()
trả về thời gian của tick cuối cùng, không thay đổi sau khi sử dụng Sleep()
. Chạy EA trong chế độ "Chỉ giá mở", và bạn sẽ thấy thông báo về việc đồng bộ hóa các thanh.
Sử dụng hàm TimeTradeServer()
thay vì TimeCurrent()
, nếu bạn cần lấy thời gian máy chủ hiện tại, chứ không phải thời gian đến của tick cuối cùng.
Có một cách khác để đồng bộ hóa thanh - sử dụng bộ đếm thời gian. Một ví dụ về EA như vậy là Synchronize_Bars_Use_OnTimer.mq5
, được đính kèm với bài viết này.
Hàm IndicatorRelease()
trong Tester
Sau khi hoàn thành một lần kiểm tra đơn lẻ, một biểu đồ của công cụ sẽ tự động được mở, hiển thị các giao dịch đã hoàn thành và các chỉ báo được sử dụng trong EA. Điều này giúp kiểm tra trực quan các điểm vào và ra, và so sánh chúng với giá trị của các chỉ báo.
Lưu ý: các chỉ báo hiển thị trên biểu đồ, tự động mở sau khi hoàn thành kiểm tra, được tính toán lại sau khi kiểm tra hoàn tất. Ngay cả khi các chỉ báo này đã được sử dụng trong EA được kiểm tra. Nhưng trong một số trường hợp, lập trình viên có thể muốn ẩn thông tin về các chỉ báo được sử dụng trong các thuật toán giao dịch. Ví dụ, mã của EA được cho thuê hoặc bán dưới dạng tệp thực thi, mà không cung cấp mã nguồn. Để phục vụ mục đích này, hàm
IndicatorRelease()
là phù hợp.
Nếu terminal đặt một mẫu với tên tester.tpl
trong thư mục /profiles/templates
của terminal client, thì nó sẽ được áp dụng cho biểu đồ được mở. Nếu không có, mẫu mặc định sẽ được áp dụng (default.tpl
).
Hàm IndicatorRelease()
ban đầu được thiết kế để giải phóng phần tính toán của chỉ báo nếu nó không còn cần thiết nữa. Điều này cho phép tiết kiệm cả bộ nhớ và tài nguyên CPU, vì mỗi tick đều gọi tính toán chỉ báo. Mục đích thứ hai của nó là cấm hiển thị chỉ báo trên biểu đồ kiểm tra sau khi chạy một lần kiểm tra đơn lẻ.
Để cấm hiển thị chỉ báo trên biểu đồ sau khi kiểm tra, hãy gọi hàm IndicatorRelease()
với tay cầm của chỉ báo trong trình xử lý OnDeinit()
. Hàm OnDeinit()
luôn được gọi sau khi hoàn thành và trước khi hiển thị biểu đồ kiểm tra.
//+------------------------------------------------------------------+
//| Hàm hủy khởi tạo Expert |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
bool hidden=IndicatorRelease(handle_ind);
if(hidden) Print("IndicatorRelease() đã hoàn thành thành công");
else Print("IndicatorRelease() trả về false. Mã lỗi ",GetLastError());
}
2
3
4
5
6
7
8
9
10
Để cấm hiển thị chỉ báo trên biểu đồ sau khi hoàn thành một lần kiểm tra đơn lẻ, hãy sử dụng hàm IndicatorRelease()
trong trình xử lý OnDeinit()
.
Xử lý sự kiện trong Tester
Sự hiện diện của trình xử lý OnTick()
trong EA không phải là bắt buộc để nó được kiểm tra trên dữ liệu lịch sử trong tester MetaTrader 5. Chỉ cần EA chứa ít nhất một trong các hàm xử lý sau là đủ:
OnTick()
- Trình xử lý sự kiện khi có tick mới đến;OnTrade()
- Trình xử lý sự kiện giao dịch;OnTimer()
- Trình xử lý sự kiện khi có tín hiệu từ bộ đếm thời gian;OnChartEvent()
- Trình xử lý cho các sự kiện client.
Khi kiểm tra trong EA, chúng ta có thể xử lý các sự kiện tùy chỉnh bằng hàm OnChartEvent()
, nhưng trong các chỉ báo, hàm này không thể được gọi trong tester. Ngay cả khi chỉ báo có trình xử lý sự kiện OnChartEvent()
và chỉ báo này được sử dụng trong EA được kiểm tra, bản thân chỉ báo sẽ không nhận được bất kỳ sự kiện tùy chỉnh nào.
Trong quá trình kiểm tra, một chỉ báo có thể tạo ra các sự kiện tùy chỉnh bằng hàm EventChartCustom()
, và EA có thể xử lý sự kiện này trong OnChartEvent()
.
Ngoài các sự kiện này, các sự kiện đặc biệt liên quan đến quá trình kiểm tra và tối ưu hóa được tạo ra trong Strategy Tester:
- Tester - Sự kiện này được tạo sau khi hoàn thành kiểm tra Expert Advisor trên dữ liệu lịch sử. Sự kiện Tester được xử lý bằng hàm
OnTester()
. Hàm này chỉ có thể được sử dụng khi kiểm tra Expert Advisor và chủ yếu nhằm tính toán một giá trị được sử dụng làm tiêu chí tối đa tùy chỉnh cho việc tối ưu hóa di truyền của các tham số đầu vào. - TesterInit - Sự kiện này được tạo khi bắt đầu tối ưu hóa trong Strategy Tester trước lần chạy đầu tiên. Sự kiện TesterInit được xử lý bằng hàm
OnTesterInit()
. Khi bắt đầu tối ưu hóa, một Expert Advisor với trình xử lý này sẽ tự động được tải trên một biểu đồ terminal riêng với biểu tượng và kỳ được chỉ định trong tester, và nhận sự kiện TesterInit. Hàm này được sử dụng để khởi tạo một Expert Advisor trước khi bắt đầu tối ưu hóa để tiếp tục xử lý kết quả tối ưu hóa. - TesterPass - Sự kiện này được tạo khi nhận được một khung dữ liệu mới. Sự kiện TesterPass được xử lý bằng hàm
OnTesterPass()
. Một Expert Advisor với trình xử lý này sẽ tự động được tải trên một biểu đồ terminal riêng với biểu tượng/kỳ được chỉ định để kiểm tra, và nhận sự kiện TesterPass khi một khung được nhận trong quá trình tối ưu hóa. Hàm này được sử dụng để xử lý động kết quả tối ưu hóa "tại chỗ" mà không cần đợi hoàn thành. Các khung được thêm bằng hàmFrameAdd()
, có thể được gọi sau khi kết thúc một lần chạy đơn lẻ trong trình xử lýOnTester()
. - TesterDeinit - Sự kiện này được tạo sau khi kết thúc tối ưu hóa Expert Advisor trong Strategy Tester. Sự kiện TesterDeinit được xử lý bằng hàm
OnTesterDeinit()
. Một Expert Advisor với trình xử lý này sẽ tự động được tải trên một biểu đồ khi bắt đầu tối ưu hóa, và nhận TesterDeinit sau khi hoàn thành. Hàm này được sử dụng để xử lý cuối cùng tất cả kết quả tối ưu hóa.
Tác nhân kiểm tra
Kiểm tra trong terminal client MetaTrader 5 được thực hiện bằng tác nhân kiểm tra. Các tác nhân cục bộ được tạo và kích hoạt tự động. Số lượng tác nhân cục bộ mặc định tương ứng với số lõi của máy tính.
Mỗi tác nhân kiểm tra có bản sao riêng của biến toàn cục, không liên quan đến terminal client. Bản thân terminal là bộ điều phối, phân phối các nhiệm vụ cho các tác nhân cục bộ và từ xa. Sau khi thực hiện một nhiệm vụ kiểm tra EA với các tham số đã cho, tác nhân trả kết quả về terminal. Với một lần kiểm tra đơn lẻ, chỉ một tác nhân được sử dụng.
Tác nhân lưu trữ lịch sử nhận được từ terminal trong các thư mục riêng, theo tên của công cụ, vì vậy lịch sử cho EURUSD được lưu trong thư mục có tên EURUSD
. Ngoài ra, lịch sử của các công cụ được phân tách theo nguồn của chúng. Cấu trúc lưu trữ lịch sử trông như sau:
tester_catalog\Agent-IPaddress-Port\bases\name_source\history\symbol_name
Ví dụ, lịch sử cho EURUSD từ máy chủ MetaQuotes-Demo có thể được lưu trong thư mục tester_catalog\Agent-127.0.0.1-3000\bases\MetaQuotes-Demo\EURUSD
.
Một tác nhân cục bộ, sau khi hoàn thành kiểm tra, chuyển sang chế độ chờ, đợi nhiệm vụ tiếp theo trong 5 phút nữa, để không lãng phí thời gian khởi động cho lần gọi tiếp theo. Chỉ sau khi thời gian chờ kết thúc, tác nhân cục bộ mới tắt và được giải phóng khỏi bộ nhớ CPU.
Trong trường hợp hoàn thành kiểm tra sớm từ phía người dùng (nút "Hủy"), cũng như khi đóng terminal client, tất cả các tác nhân cục bộ ngay lập tức dừng công việc và được giải phóng khỏi bộ nhớ.
Trao đổi dữ liệu giữa Terminal và Tác nhân
Khi bạn chạy một bài kiểm tra, terminal client chuẩn bị gửi cho tác nhân một số khối tham số:
- Tham số đầu vào để kiểm tra (chế độ mô phỏng, khoảng thời gian kiểm tra, công cụ, tiêu chí tối ưu hóa, v.v.)
- Danh sách các biểu tượng đã chọn trong "Market Watch"
- Đặc điểm của biểu tượng kiểm tra (kích thước hợp đồng, mức ký quỹ cho phép từ thị trường để đặt StopLoss và TakeProfit, v.v.)
- Expert Advisor cần kiểm tra và giá trị của các tham số đầu vào của nó
- Thông tin về các tệp bổ sung (thư viện, chỉ báo, tệp dữ liệu -
#property tester_ ...
)
tester_indicator | string | Tên của chỉ báo tùy chỉnh dưới định dạng "indicator_name.ex5". Các chỉ báo cần kiểm tra được xác định tự động từ lời gọi hàm iCustom() , nếu tham số tương ứng được đặt qua chuỗi hằng. Đối với tất cả các trường hợp khác (sử dụng hàm IndicatorCreate() hoặc sử dụng chuỗi không hằng trong tham số đặt tên chỉ báo), thuộc tính này là bắt buộc |
---|---|---|
tester_file | string | Tên tệp cho tester với chỉ định phần mở rộng, trong dấu ngoặc kép (dưới dạng chuỗi hằng). Tệp được chỉ định sẽ được truyền cho tester. Các tệp đầu vào cần kiểm tra, nếu có, phải luôn được chỉ định |
tester_library | string | Tên thư viện với phần mở rộng, trong dấu ngoặc kép. Thư viện có thể có phần mở rộng dll hoặc ex5. Các thư viện cần kiểm tra được xác định tự động. Tuy nhiên, nếu bất kỳ thư viện nào được sử dụng bởi một chỉ báo tùy chỉnh, thuộc tính này là bắt buộc |
Đối với mỗi khối tham số, một dấu vân tay số dưới dạng hàm băm MD5 được tạo ra, được gửi đến tác nhân. Hàm băm MD5 là duy nhất cho mỗi tập hợp, khối lượng của nó nhỏ hơn nhiều lần so với lượng thông tin mà nó được tính toán.
Tác nhân nhận hàm băm của các khối và so sánh chúng với những gì nó đã có. Nếu dấu vân tay của khối tham số đã cho không có trong tác nhân, hoặc hàm băm nhận được khác với hàm băm hiện có, tác nhân yêu cầu khối tham số này. Điều này giảm lưu lượng giữa terminal và tác nhân.
Sau khi kiểm tra, tác nhân trả về terminal tất cả kết quả của lần chạy, được hiển thị trong các tab "Kết quả kiểm tra" và "Kết quả tối ưu hóa": lợi nhuận nhận được, số lượng giao dịch, hệ số Sharpe, kết quả của hàm OnTester()
, v.v.
Trong quá trình tối ưu hóa, terminal giao các nhiệm vụ kiểm tra cho các tác nhân theo các gói nhỏ, mỗi gói chứa một vài nhiệm vụ (mỗi nhiệm vụ nghĩa là kiểm tra đơn lẻ với một tập hợp tham số đầu vào). Điều này giảm thời gian trao đổi giữa terminal và tác nhân.
Các tác nhân không bao giờ ghi vào ổ cứng các tệp EX5 nhận được từ terminal (EA, chỉ báo, thư viện, v.v.) vì lý do bảo mật, để máy tính chạy tác nhân không thể sử dụng dữ liệu đã gửi. Tất cả các tệp khác, bao gồm DLL, được ghi trong sandbox. Trong các tác nhân từ xa, bạn không thể kiểm tra EA sử dụng DLL.
Kết quả kiểm tra được terminal tổng hợp vào một bộ nhớ đệm kết quả đặc biệt (bộ nhớ đệm kết quả), để truy cập nhanh khi cần. Đối với mỗi tập hợp tham số, terminal tìm kiếm trong bộ nhớ đệm kết quả các kết quả đã có từ các lần chạy trước, để tránh chạy lại. Nếu không tìm thấy kết quả với tập hợp tham số đó, tác nhân được giao nhiệm vụ thực hiện kiểm tra.
Tất cả lưu lượng giữa terminal và tác nhân đều được mã hóa.
Tick không được gửi qua mạng, chúng được tạo trên các tác nhân kiểm tra.
Sử dụng thư mục chung của tất cả các Terminal Client
Tất cả các tác nhân kiểm tra đều được cô lập với nhau và với terminal client: mỗi tác nhân có thư mục riêng nơi nhật ký của nó được ghi lại. Ngoài ra, tất cả các thao tác tệp trong quá trình kiểm tra của tác nhân diễn ra trong thư mục agent_name/MQL5/Files
. Tuy nhiên, chúng ta có thể thực hiện tương tác giữa các tác nhân cục bộ và terminal client thông qua một thư mục chung cho tất cả các terminal client, nếu trong khi mở tệp, bạn chỉ định cờ FILE_COMMON
:
//+------------------------------------------------------------------+
//| Hàm khởi tạo Expert |
//+------------------------------------------------------------------+
int OnInit()
{
//--- thư mục chung cho tất cả các terminal client
common_folder=TerminalInfoString(TERMINAL_COMMONDATA_PATH);
//--- hiển thị tên của thư mục này
PrintFormat("Mở tệp trong thư mục chung của các terminal client %s", common_folder);
//--- mở một tệp trong thư mục chung (được chỉ định bởi cờ FILE_COMMON)
handle=FileOpen(filename,FILE_WRITE|FILE_READ|FILE_COMMON);
... các hành động tiếp theo
//---
return(INIT_SUCCEEDED);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Sử dụng DLL
Để tăng tốc độ tối ưu hóa, chúng ta có thể sử dụng không chỉ các tác nhân cục bộ mà còn cả tác nhân từ xa. Trong trường hợp này, có một số hạn chế đối với các tác nhân từ xa. Trước hết, các tác nhân từ xa không hiển thị trong nhật ký của họ kết quả thực thi của hàm Print()
, thông báo về việc mở và đóng vị thế. Một lượng thông tin tối thiểu được hiển thị trong nhật ký để ngăn các EA được viết không chính xác làm rối máy tính mà tác nhân từ xa đang hoạt động bằng các thông báo.
Hạn chế thứ hai - cấm sử dụng DLL khi kiểm tra EA. Các lời gọi DLL bị cấm tuyệt đối trên các tác nhân từ xa vì lý do bảo mật. Trên tác nhân cục bộ, các lời gọi DLL trong EA được kiểm tra chỉ được phép với quyền "Cho phép nhập DLL" thích hợp.
Lưu ý: Khi sử dụng EA của bên thứ ba (script, chỉ báo) yêu cầu các lời gọi DLL được phép, bạn nên nhận thức được các rủi ro mà bạn chấp nhận khi bật tùy chọn này trong cài đặt của terminal. Bất kể EA sẽ được sử dụng như thế nào - để kiểm tra hay để chạy trên biểu đồ.