Theo dõi sự hình thành thanh
Chỉ số IndUnityPercent.mq5
được thảo luận trong phần trước được tính toán lại trên thanh cuối cùng sau mỗi tick vì nó sử dụng giá Close
. Một số chỉ số và Expert Advisors được phát triển đặc biệt theo phong cách tiết kiệm hơn, với một lần tính toán duy nhất trên mỗi thanh. Ví dụ, chúng ta có thể tính toán công thức Unity tại giá mở cửa, và khi đó việc bỏ qua các tick là hợp lý. Có một số cách để phát hiện một thanh mới:
- Ghi nhớ thời gian của thanh 0 hiện tại (thông qua tham số
time
của hàmOnCalculate
—time[0]
hoặc tổng quát hơn làiTime(symbol, period, 0)
) và đợi nó thay đổi. - Ghi nhớ số lượng thanh
rates_total
(hoặciBars(symbol, period)
) và phản hồi khi tăng thêm 1 (sự thay đổi sang một số lượng khác theo một hướng hoặc hướng khác là đáng ngờ và có thể chỉ ra sự sửa đổi lịch sử). - Chờ một thanh có khối lượng tick bằng 1 (tick đầu tiên trên thanh).
Tuy nhiên, với tính chất đa tiền tệ của chỉ số, khái niệm về sự hình thành của một thanh mới trở nên không còn rõ ràng như vậy.
Trên mỗi ký hiệu, thanh tiếp theo xuất hiện khi có tick của chính nó, và chúng thường có thời gian đến khác nhau. Trong trường hợp này, nhà phát triển chỉ số phải quyết định cách hành động: liệu có nên đợi sự xuất hiện của các thanh có cùng thời gian trên tất cả các ký hiệu hay tính toán lại chỉ số trên các thanh cuối cùng nhiều lần sau khi xuất hiện một thanh mới trên bất kỳ ký hiệu nào.
Trong phần này, chúng ta sẽ giới thiệu một lớp đơn giản MultiSymbolMonitor
(xem tệp MultiSymbolMonitor.mqh
) để theo dõi sự hình thành của các thanh mới theo một danh sách các ký hiệu đã cho.
Khung thời gian cần thiết có thể được truyền vào hàm khởi tạo của lớp. Theo mặc định, nó theo dõi khung thời gian của biểu đồ hiện tại, nơi chương trình đang chạy.
class MultiSymbolMonitor
{
protected:
ENUM_TIMEFRAMES period;
public:
MultiSymbolMonitor(): period(_Period) {}
MultiSymbolMonitor(const ENUM_TIMEFRAMES p): period(p) {}
...
}
2
3
4
5
6
7
8
9
10
Để lưu trữ danh sách các ký hiệu được theo dõi, chúng ta sẽ sử dụng một lớp phụ trợ MapArray
từ phần trước. Trong mảng này, chúng ta sẽ ghi các cặp [tên ký hiệu; dấu thời gian của thanh cuối cùng], tức là các kiểu mẫu string,datetime
. Phương thức attach
điền dữ liệu vào mảng.
protected:
MapArray<string,datetime> lastTime;
...
public:
void attach(const string symbol)
{
lastTime.put(symbol, NULL);
}
2
3
4
5
6
7
8
Đối với một mảng đã cho, lớp có thể cập nhật và kiểm tra dấu thời gian trong phương thức check
bằng cách gọi hàm iTime
trong một vòng lặp qua các ký hiệu.
ulong check(const bool refresh = false)
{
ulong flags = 0;
for(int i = 0; i < lastTime.getSize(); i++)
{
const string symbol = lastTime.getKey(i);
const datetime dt = iTime(symbol, period, 0);
if(dt != lastTime[symbol]) // có thay đổi nào không?
{
flags |= 1 << i;
}
if(refresh) // cập nhật dấu thời gian
{
lastTime.put(symbol, dt);
}
}
return flags;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Mã gọi nên gọi check
theo quyết định của nó, thường là khi có tick đến, hoặc theo bộ đếm thời gian. Nói một cách nghiêm ngặt, cả hai tùy chọn này không cung cấp phản ứng tức thì với sự xuất hiện của các tick (và các thanh mới) trên các công cụ khác vì sự kiện OnCalculate
chỉ xuất hiện trên các tick của ký hiệu đang hoạt động của biểu đồ, và nếu có một tick của ký hiệu khác giữa chúng, chúng ta sẽ không biết về nó cho đến tick "riêng" tiếp theo.
Chúng ta sẽ xem xét việc giám sát thời gian thực của các tick từ nhiều công cụ trong chương về các sự kiện biểu đồ tương tác (xem chỉ số gián điệp EventTickSpy.mq5
trong phần Tạo sự kiện tùy chỉnh).
Hiện tại, chúng ta sẽ kiểm tra các thanh với độ chính xác có sẵn. Vì vậy, hãy tiếp tục với phương thức check
.
Mỗi điểm thời gian được đặc trưng bởi trạng thái riêng của tập hợp dấu thời gian cho tất cả các ký hiệu trong mảng. Ví dụ, một thanh mới có thể hình thành lúc 12:00 chỉ cho công cụ có thanh khoản cao nhất, và đối với một số công cụ khác, các tick sẽ xuất hiện sau vài mili giây hoặc thậm chí vài giây. Trong khoảng thời gian này, một phần tử sẽ được cập nhật trong mảng, và phần còn lại sẽ là cũ. Sau đó, dần dần tất cả các ký hiệu sẽ có thanh 12:00.
Đối với tất cả các ký hiệu mà thời gian mở của thanh cuối cùng không bằng với thời gian đã lưu, phương thức đặt bit với số ký hiệu, do đó hình thành một mặt nạ bit với các thay đổi. Danh sách không được chứa quá 64 ký hiệu.
Nếu giá trị trả về là zero, không có thay đổi nào được đăng ký.
Tham số refresh
chỉ định liệu phương thức check
sẽ chỉ đăng ký các thay đổi (false
), hay sẽ cập nhật trạng thái theo tình hình thị trường hiện tại (true
).
Phương thức describe
cho phép bạn lấy danh sách các ký hiệu đã thay đổi bằng một mặt nạ bit.
string describe(ulong flags = 0)
{
string message = "";
if(flags == 0) flags = check();
for(int i = 0; i < lastTime.getSize(); i++)
{
if((flags & (1 << i)) != 0)
{
message += lastTime.getKey(i) + "\t";
}
}
return message;
}
2
3
4
5
6
7
8
9
10
11
12
13
Tiếp theo, chúng ta sẽ sử dụng inSync
để xác định xem tất cả các ký hiệu trong mảng có cùng thời gian thanh cuối cùng hay không. Điều này chỉ có ý nghĩa khi sử dụng cho một tập hợp các loại tiền tệ có cùng phiên giao dịch.
bool inSync() const
{
if(lastTime.getSize() == 0) return false;
const datetime first = lastTime[0];
for(int i = 1; i < lastTime.getSize(); i++)
{
if(first != lastTime[i]) return false;
}
return true;
}
2
3
4
5
6
7
8
9
10
Sử dụng lớp đã mô tả, chúng ta triển khai một chỉ số đa tiền tệ đơn giản IndMultiSymbolMonitor.cpp
, nhiệm vụ duy nhất của nó sẽ là phát hiện các thanh mới cho một danh sách các ký hiệu.
Vì không có vẽ được cung cấp cho chỉ số, số lượng bộ đệm và biểu đồ là 0.
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots 0
2
3
Danh sách các công cụ được chỉ định trong biến đầu vào tương ứng và sau đó được chuyển đổi thành một mảng được đăng ký trong đối tượng monitor
.
input string Instruments = "EURUSD,GBPUSD,USDCHF,USDJPY,AUDUSD,USDCAD,NZDUSD";
#include <MQL5Book/MultiSymbolMonitor.mqh>
MultiSymbolMonitor monitor;
void OnInit()
{
string symbols[];
const int n = StringSplit(Instruments, ',', symbols);
for(int i = 0; i < n; ++i)
{
monitor.attach(symbols[i]);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Trình xử lý OnCalculate
gọi monitor trên các tick và xuất các thay đổi trạng thái vào nhật ký.
int OnCalculate(const int rates_total,
const int prev_calculated,
const int begin,
const double &price[])
{
const ulong changes = monitor.check(true);
if(changes != 0)
{
Print("New bar(s) on: ", monitor.describe(changes),
", in-sync:", monitor.inSync());
}
return rates_total;
}
2
3
4
5
6
7
8
9
10
11
12
13
Để kiểm tra chỉ số này, chúng ta sẽ cần dành nhiều thời gian trực tuyến trong terminal. Tuy nhiên, MetaTrader 5 cho phép thực hiện điều này dễ dàng hơn nhiều — với sự trợ giúp của một bộ kiểm tra. Chúng ta sẽ làm điều này trong phần tiếp theo.