Hướng lập chỉ mục chuỗi thời gian trong mảng
Do đặc thù giao dịch được áp dụng, MQL5 mang đến các tính năng bổ sung khi làm việc với mảng. Một trong số đó là các phần tử mảng có thể chứa dữ liệu tương ứng với các điểm thời gian. Ví dụ, bao gồm các mảng chứa báo giá của công cụ tài chính, giá tick và giá trị của các chỉ báo kỹ thuật. Thứ tự thời gian của dữ liệu có nghĩa là các phần tử mới liên tục được thêm vào cuối mảng và chỉ số của chúng tăng lên.
Tuy nhiên, từ quan điểm giao dịch, việc đếm từ hiện tại về quá khứ sẽ thuận tiện hơn. Khi đó, phần tử 0 luôn chứa giá trị mới nhất, cập nhật nhất, phần tử 1 luôn chứa giá trị trước đó, và cứ tiếp tục như vậy.
MQL5 cho phép bạn chọn và chuyển đổi hướng lập chỉ mục của mảng ngay trong quá trình thực thi. Một mảng được đánh số từ hiện tại về quá khứ được gọi là chuỗi thời gian (timeseries). Nếu chỉ số tăng từ quá khứ đến hiện tại, đây là một mảng thông thường. Trong chuỗi thời gian, thời gian giảm khi chỉ số tăng. Trong các mảng thông thường, thời gian tăng, giống như trong thực tế.
Điều quan trọng cần lưu ý là một mảng không nhất thiết phải chứa các giá trị liên quan đến thời gian để có thể chuyển đổi thứ tự định địa chỉ. Chỉ là tính năng này được yêu cầu nhiều nhất và thực tế đã xuất hiện để làm việc với dữ liệu lịch sử.
Thuộc tính này của mảng không ảnh hưởng đến cách bố trí dữ liệu trong bộ nhớ. Chỉ có thứ tự đánh số thay đổi. Cụ thể, chúng ta có thể tự triển khai một phiên bản tương tự trong MQL5 bằng cách duyệt mảng theo vòng lặp "từ cuối về đầu". Nhưng MQL5 cung cấp các hàm sẵn có để che giấu toàn bộ quy trình lặp lại này khỏi các lập trình viên ứng dụng.
Chuỗi thời gian có thể là bất kỳ mảng động một chiều nào được mô tả trong chương trình MQL, cũng như các mảng bên ngoài được truyền vào chương trình MQL từ lõi MetaTrader 5, chẳng hạn như tham số của các hàm tiện ích. Ví dụ, một loại chương trình MQL đặc biệt, chỉ báo, nhận các mảng chứa dữ liệu giá của biểu đồ hiện tại trong trình xử lý sự kiện OnCalculate
. Chúng ta sẽ nghiên cứu tất cả các tính năng sử dụng thực tế của chuỗi thời gian sau, trong Phần thứ năm của cuốn sách.
Các mảng được định nghĩa trong chương trình MQL không phải là chuỗi thời gian theo mặc định.
Hãy xem xét một tập hợp các hàm để xác định và thay đổi thuộc tính "chuỗi" của mảng, cũng như "sự thuộc về" của nó đối với terminal. Script tổng quát ArrayAsSeries.mq5
với các ví dụ sẽ được cung cấp sau phần mô tả.
bool ArrayIsSeries(const void &array[])
Hàm này trả về dấu hiệu liệu mảng được chỉ định có phải là một chuỗi thời gian "thực sự" hay không, tức là nó được kiểm soát và cung cấp bởi chính terminal. Bạn không thể thay đổi đặc tính này của mảng. Các mảng như vậy chỉ khả dụng cho chương trình MQL ở chế độ "chỉ đọc".
Trong tài liệu MQL5, các thuật ngữ "timeseries" và "series" được sử dụng để mô tả cả việc lập chỉ mục ngược của mảng và thực tế rằng mảng có thể "thuộc về" terminal (terminal cấp phát bộ nhớ cho nó và điền dữ liệu). Trong cuốn sách, chúng ta sẽ cố gắng tránh sự mơ hồ này và gọi các mảng có lập chỉ mục ngược là "chuỗi thời gian". Còn các mảng của terminal sẽ chỉ được gọi là mảng
own
của terminal.
Bạn có thể thay đổi cách lập chỉ mục của bất kỳ mảng tùy chỉnh nào của terminal theo ý muốn bằng cách chuyển nó sang chế độ chuỗi thời gian hoặc trở lại chế độ tiêu chuẩn. Điều này được thực hiện bằng hàm ArraySetAsSeries
, áp dụng không chỉ cho mảng của chính terminal mà còn cho các mảng động tùy chỉnh (xem bên dưới).
bool ArrayGetAsSeries(const void &array[])
Hàm này trả về dấu hiệu liệu chế độ lập chỉ mục chuỗi thời gian có được bật cho mảng được chỉ định hay không, tức là chỉ số tăng theo hướng từ hiện tại về quá khứ. Bạn có thể thay đổi hướng lập chỉ mục bằng hàm ArraySetAsSeries
.
Hướng lập chỉ mục ảnh hưởng đến các giá trị được trả về bởi các hàm ArrayBsearch
, ArrayMaximum
và ArrayMinimum
(xem phần So sánh, sắp xếp và tìm kiếm trong mảng).
bool ArraySetAsSeries(const void &array[], bool as_series)
Hàm này đặt hướng lập chỉ mục trong mảng theo tham số as_series
: giá trị true
có nghĩa là thứ tự lập chỉ mục ngược, trong khi false
có nghĩa là thứ tự thông thường của các phần tử.
Hàm trả về true
khi thiết lập thuộc tính thành công, hoặc false
nếu xảy ra lỗi.
Hỗ trợ mảng của bất kỳ loại nào, nhưng việc thay đổi hướng lập chỉ mục bị cấm đối với mảng đa chiều và mảng có kích thước cố định.
Script ArrayAsSeries.mq5
mô tả một số mảng nhỏ để thử nghiệm với các hàm trên.
#define LIMIT 10
template<typename T>
void indexArray(T &array[])
{
for(int i = 0; i < ArraySize(array); ++i)
{
array[i] = (T)(i + 1);
}
}
class Dummy
{
int data[];
};
void OnStart()
{
double array2D[][2];
double fixed[LIMIT];
double dynamic[];
MqlRates rates[];
Dummy dummies[];
ArrayResize(dynamic, LIMIT); // cấp phát bộ nhớ
// điền một vài mảng với các số: 1, 2, 3,...
indexArray(fixed);
indexArray(dynamic);
...
}
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
Chúng ta có một mảng hai chiều array2D
, mảng cố định và mảng động, tất cả đều thuộc loại double
, cũng như các mảng của cấu trúc và đối tượng lớp. Các mảng fixed
và dynamic
được điền bằng các số nguyên liên tiếp (sử dụng hàm trợ giúp indexArray
) để minh họa. Đối với các loại mảng khác, chúng ta sẽ chỉ kiểm tra tính áp dụng của chế độ "chuỗi", vì ý tưởng về hiệu ứng lập chỉ mục ngược sẽ rõ ràng từ ví dụ của các mảng đã được điền.
Đầu tiên, hãy đảm bảo rằng không có mảng nào là mảng riêng của terminal:
PRTS(ArrayIsSeries(array2D)); // false
PRTS(ArrayIsSeries(fixed)); // false
PRTS(ArrayIsSeries(dynamic)); // false
PRTS(ArrayIsSeries(rates)); // false
2
3
4
Tất cả các lệnh gọi ArrayIsSeries
trả về false
vì chúng ta đã định nghĩa tất cả các mảng trong chương trình MQL. Chúng ta sẽ thấy giá trị true
cho các mảng tham số của hàm OnCalculate
trong các chỉ báo (trong Phần thứ năm).
Tiếp theo, hãy kiểm tra hướng lập chỉ mục ban đầu của mảng:
PRTS(ArrayGetAsSeries(array2D)); // false, không thể là true
PRTS(ArrayGetAsSeries(fixed)); // false
PRTS(ArrayGetAsSeries(dynamic)); // false
PRTS(ArrayGetAsSeries(rates)); // false
PRTS(ArrayGetAsSeries(dummies)); // false
2
3
4
5
Và một lần nữa, chúng ta sẽ nhận được false
ở mọi nơi.
Hãy xuất các mảng fixed
và dynamic
vào nhật ký để xem thứ tự ban đầu của các phần tử.
ArrayPrint(fixed, 1);
ArrayPrint(dynamic, 1);
/*
1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0
1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0
*/
2
3
4
5
6
Bây giờ chúng ta thử thay đổi thứ tự lập chỉ mục:
// lỗi: không cho phép chuyển đổi tham số
// PRTS(ArraySetAsSeries(array2D, true));
// cảnh báo: không thể sử dụng cho mảng cấp phát tĩnh
PRTS(ArraySetAsSeries(fixed, true)); // false
// sau đây mọi thứ diễn ra bình thường
PRTS(ArraySetAsSeries(dynamic, true)); // true
PRTS(ArraySetAsSeries(rates, true)); // true
PRTS(ArraySetAsSeries(dummies, true)); // true
2
3
4
5
6
7
8
9
10
Câu lệnh cho mảng array2D
gây ra lỗi biên dịch và do đó được chú thích. Câu lệnh cho mảng fixed
đưa ra cảnh báo trình biên dịch rằng nó không thể áp dụng cho mảng có kích thước cố định. Khi chạy, ba câu lệnh cuối cùng trả về thành công (true
). Hãy xem thuộc tính của các mảng đã thay đổi như thế nào:
// kiểm tra thuộc tính:
// đầu tiên, liệu chúng có phải là mảng riêng của terminal không
PRTS(ArrayIsSeries(fixed)); // false
PRTS(ArrayIsSeries(dynamic)); // false
PRTS(ArrayIsSeries(rates)); // false
PRTS(ArrayIsSeries(dummies)); // false
// thứ hai, hướng lập chỉ mục
PRTS(ArrayGetAsSeries(fixed)); // false
PRTS(ArrayGetAsSeries(dynamic)); // true
PRTS(ArrayGetAsSeries(rates)); // true
PRTS(ArrayGetAsSeries(dummies)); // true
2
3
4
5
6
7
8
9
10
11
12
Như dự kiến, các mảng không biến thành mảng riêng của terminal. Tuy nhiên, ba trong số bốn mảng đã thay đổi cách lập chỉ mục sang chế độ chuỗi thời gian, bao gồm mảng của cấu trúc và đối tượng. Để minh họa kết quả, các mảng fixed
và dynamic
lại được hiển thị trong nhật ký.
ArrayPrint(fixed, 1); // không thay đổi
ArrayPrint(dynamic, 1); // thứ tự ngược lại
/*
1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0
10.0 9.0 8.0 7.0 6.0 5.0 4.0 3.0 2.0 1.0
*/
2
3
4
5
6
Vì chế độ không được áp dụng cho mảng có kích thước cố định, nó vẫn không thay đổi. Mảng dynamic
giờ đây được hiển thị theo thứ tự ngược lại.
Nếu bạn đặt mảng vào chế độ lập chỉ mục ngược, thay đổi kích thước của nó, rồi quay lại lập chỉ mục trước đó, thì các phần tử được thêm vào sẽ được chèn vào đầu mảng.