Tổng quan về các hàm Copy để lấy mảng báo giá
API MQL5 chứa một số hàm để đọc chuỗi thời gian báo giá vào mảng. Tên của chúng được liệt kê trong bảng sau.
Hàm | Hành động |
---|---|
CopyRates | Lấy lịch sử báo giá vào mảng cấu trúc MqlRates |
CopyTime | Lấy lịch sử thời gian mở thanh vào mảng kiểu datetime |
CopyOpen | Lấy lịch sử giá mở thanh vào mảng kiểu double |
CopyHigh | Lấy lịch sử giá cao nhất của thanh vào mảng kiểu double |
CopyLow | Lấy lịch sử giá thấp nhất của thanh vào mảng kiểu double |
CopyClose | Lấy lịch sử giá đóng thanh vào mảng kiểu double |
CopyTickVolume | Lấy lịch sử khối lượng tick vào mảng kiểu long |
CopyRealVolume | Lấy lịch sử khối lượng giao dịch vào mảng kiểu long |
CopySpread | Lấy lịch sử chênh lệch giá vào mảng kiểu int |
Tất cả các hàm đều nhận hai tham số đầu tiên là tên của ký hiệu mong muốn và chu kỳ, có thể được biểu diễn một cách có điều kiện bằng mã giả sau:
int Copy***(const string symbol, ENUM_TIMEFRAMES timeframe, ...)
Ngoài ra, tất cả các hàm có ba biến thể của nguyên mẫu, khác nhau ở cách đặt phạm vi yêu cầu:
- Chỉ số thanh ban đầu và số lượng thanh:
Copy***(..., int offset, int count, ...)
- Thời gian bắt đầu phạm vi và số lượng thanh:
Copy***(..., datetime start, int count, ...)
- Thời gian bắt đầu và kết thúc phạm vi:
Copy***(..., datetime start, datetime stop, ...)
Đồng thời, ký hiệu tham số ngụ ý rằng dữ liệu được yêu cầu có hướng lập chỉ mục như trong chuỗi thời gian, tức là vị trí offset
với chỉ số 0 lưu trữ dữ liệu của thanh chưa hoàn thành hiện tại, và việc tăng chỉ số tương ứng với việc di chuyển sâu hơn vào lịch sử giá. Do đó, đặc biệt ở tùy chọn thứ hai, số lượng thanh được chỉ định count
sẽ được đếm ngược từ điểm bắt đầu của phạm vi offset
, tức là theo hướng giảm thời gian.
Tùy chọn thứ ba cung cấp thêm sự linh hoạt: không quan trọng thứ tự của ngày bắt đầu và kết thúc (start/stop
), vì các hàm sẽ trả về dữ liệu trong phạm vi từ ngày nhỏ hơn đến ngày lớn hơn trong mọi trường hợp. Các thanh phù hợp được chọn sao cho thời gian mở của chúng nằm giữa các mốc thời gian start/stop
hoặc bằng một trong số chúng, tức là phạm vi [start; stop]
được xem xét bao gồm cả hai ranh giới.
Việc chọn tùy chọn hàm nào phụ thuộc vào nhà phát triển dựa trên điều gì quan trọng hơn: lấy một số lượng phần tử đảm bảo (ví dụ, cho các thuật toán học máy) hay bao phủ một khoảng thời gian ngày cụ thể (ví dụ, với hành vi thị trường đồng nhất được xác định trước).
Độ chính xác biểu diễn thời gian trong kiểu datetime
là 1 giây. Các giá trị start/stop
không cần phải được làm tròn theo kích thước của chu kỳ. Ví dụ, phạm vi từ 14:59 đến 16:01 sẽ cho phép chọn hai thanh trên khung thời gian H1 cho 15:00 và 16:00. Một phạm vi suy biến với các nhãn bằng nhau và được làm tròn, ví dụ, 15:00 trong báo giá H1, tương ứng với một thanh.
Bạn có thể yêu cầu các thanh trên khung thời gian ngày ngay cả khi có giờ/phút/giây khác không trong các tham số start/stop (mặc dù nhãn thanh trên khung thời gian D1 có thời gian 00:00). Trong trường hợp này, chỉ những thanh D1 có thời gian mở sau giá trị nhỏ nhất của start/stop
và đến giá trị lớn nhất của start/stop
(không thể có sự bằng nhau với nhãn của các thanh ngày trong trường hợp này vì thời gian yêu cầu chứa giờ/phút/giây). Ví dụ, giữa D'2021.09.01 12:00'
và D'2021.09.03 07:00'
, có hai thời gian mở của thanh D1 — D'2021.09.02'
và D'2021.09.03'
. Các thanh này sẽ được bao gồm trong kết quả. Thanh D'2021.09.01'
có thời gian mở là 00:00, sớm hơn điểm bắt đầu của phạm vi và do đó bị loại bỏ. Thanh D'2021.09.03'
được bao gồm trong kết quả, mặc dù chỉ có 7 giờ buổi sáng của ngày đó nằm trong phạm vi. Mặt khác, yêu cầu vài giờ trong một ngày, ví dụ, giữa D'2021.09.01 12:00'
và D'2021.09.01 15:00'
sẽ không bao gồm một thanh ngày nào (thời gian mở của thanh D'2021.09.01'
không nằm trong phạm vi này), và do đó mảng nhận sẽ trống.
Sự khác biệt duy nhất giữa tất cả các hàm trong bảng là kiểu của mảng nhận dữ liệu, được truyền dưới dạng tham số cuối cùng bằng tham chiếu. Ví dụ, hàm CopyRates
đặt dữ liệu được yêu cầu vào mảng cấu trúc MqlRates
, và hàm CopyTime
đặt thời gian mở thanh vào mảng kiểu datetime
, v.v.
Do đó, các nguyên mẫu hàm chung có thể được biểu diễn như sau:
int Copy***(const string symbol, ENUM_TIMEFRAMES timeframe, int offset, int count, type &result[])
int Copy***(const string symbol, ENUM_TIMEFRAMES timeframe, datetime start, int count, type &result[])
int Copy***(const string symbol, ENUM_TIMEFRAMES timeframe, datetime start, datetime stop, type &result[])
Ở đây, type
khớp với bất kỳ kiểu nào trong số MqlRates
, datetime
, double
, long
hoặc int
, tùy thuộc vào hàm cụ thể.
Các hàm trả về số lượng phần tử được sao chép vào mảng hoặc -1 khi có lỗi. Đặc biệt, chúng ta sẽ nhận được -1 nếu không có dữ liệu trên máy chủ trong khoảng được yêu cầu, hoặc khoảng đó nằm ngoài số lượng thanh tối đa trên biểu đồ (TerminalInfoInteger(TERMINAL_MAXBARS)
).
Điều quan trọng cần lưu ý là trong mảng nhận, dữ liệu nhận được luôn được đặt theo thứ tự thời gian, từ quá khứ đến tương lai. Do đó, nếu sử dụng lập chỉ mục tiêu chuẩn cho mảng nhận (tức là hàm ArraySetAsSeries
), thì phần tử tại chỉ số 0 sẽ là cũ nhất và phần tử cuối cùng là mới nhất. Nếu lệnh đã được thực thi cho mảng ArraySetAsSeries(result, true)
, thì việc đánh số sẽ được thực hiện theo thứ tự ngược lại, như trong chuỗi thời gian: phần tử thứ 0 sẽ là mới nhất trong phạm vi, và phần tử cuối cùng sẽ là cũ nhất. Điều này được minh họa trong hình sau.
Chuỗi thời gian terminal và mảng nhận
Nếu thành công, số lượng phần tử được chỉ định từ chuỗi thời gian nội bộ của terminal sẽ được sao chép vào mảng đích. Khi yêu cầu dữ liệu theo phạm vi ngày (start/stop
), số lượng phần tử trong mảng kết quả sẽ được xác định gián tiếp, dựa trên nội dung lịch sử trong phạm vi này. Do đó, để sao chép một số lượng giá trị chưa biết trước, nên sử dụng mảng động: các hàm sao chép tự động phân bổ kích thước cần thiết của mảng đích (kích thước có thể được tăng hoặc giảm).
Nếu bạn cần sao chép một số lượng phần tử đã biết hoặc thực hiện thường xuyên, chẳng hạn như mỗi lần gọi OnTick
trong Expert Advisors hoặc OnCalculate
trong chỉ báo, tốt hơn là sử dụng mảng phân bố tĩnh. Thực tế là các hoạt động phân bổ bộ nhớ cho mảng động đòi hỏi thời gian bổ sung và có thể ảnh hưởng đến hiệu suất, đặc biệt trong quá trình kiểm tra và tối ưu hóa.
Chuỗi thời gian được truy cập khác nhau đối với các loại chương trình MQL khác nhau nếu dữ liệu được yêu cầu chưa sẵn sàng. Ví dụ, trong các chỉ báo tùy chỉnh, các hàm Copy
ngay lập tức trả về lỗi, vì các chỉ báo được thực thi trong luồng giao diện chung của terminal và không thể đợi dữ liệu được nhận (giả định rằng các chỉ báo sẽ yêu cầu dữ liệu trong các lần gọi tiếp theo của trình xử lý sự kiện của chúng, và chuỗi thời gian sẽ đã được tải xuống và xây dựng vào thời điểm đó). Ngoài ra, trong chương về chỉ báo, chúng ta sẽ tìm hiểu rằng để truy cập báo giá của biểu đồ "gốc" mà chỉ báo được đặt lên, nó không cần sử dụng các hàm Copy
, vì tất cả chuỗi thời gian được truyền tự động qua các tham số mảng của trình xử lý OnCalculate
.
Khi được truy cập từ Expert Advisors và scripts, một số lần thử nhận dữ liệu được thực hiện với một khoảng dừng ngắn (với việc chờ bên trong hàm), điều này cho phép thời gian để tải và tính toán các chuỗi thời gian còn thiếu. Hàm sẽ trả về lượng dữ liệu sẵn sàng vào thời điểm hết thời gian chờ này, nhưng việc tải lịch sử sẽ tiếp tục, và yêu cầu tương tự tiếp theo sẽ trả về nhiều dữ liệu hơn.
Dù trong trường hợp nào, bạn nên chuẩn bị rằng hàm Copy
sẽ trả về lỗi thay vì dữ liệu (có nhiều lý do khác nhau: lỗi kết nối, thiếu dữ liệu được yêu cầu, tải bộ xử lý nếu nhiều chuỗi thời gian mới được yêu cầu song song): phân tích nguyên nhân của vấn đề trong mã (_LastError
) và thử lại sau, điều chỉnh cài đặt, hoặc thông báo cho người dùng.
Sự hiện diện của một ký hiệu trong Market Watch
không phải là điều kiện cần thiết để yêu cầu chuỗi thời gian bằng các hàm Copy
, tuy nhiên, đối với các ký hiệu được bao gồm trong cửa sổ này, các truy vấn có xu hướng chạy nhanh hơn vì một số dữ liệu đã được tải xuống từ máy chủ và có lẽ đã được tính toán cho các chu kỳ được yêu cầu. Cách thêm ký hiệu vào Market Watch
bằng lập trình, chúng ta sẽ tìm hiểu trong phần Chỉnh sửa danh sách Market Watch.
Để giải thích nguyên tắc hoạt động của các hàm trong thực tế, hãy xem xét script SeriesCopy.mq5
. Nó chứa nhiều lần gọi hàm CopyTime
, cho phép bạn xem trực quan cách các dấu thời gian và số thanh tương quan.
Script định nghĩa một mảng động times
để nhận dữ liệu. Tất cả các yêu cầu được thực hiện cho ký hiệu "EURUSD" và khung thời gian H1.
void OnStart()
{
datetime times[];
2
3
Để bắt đầu, một yêu cầu được thực hiện cho 10 thanh, bắt đầu từ ngày 5 tháng 9 năm 2021, ngược về quá khứ. Vì ngày này là Chủ nhật, các thanh trước đó nằm vào thứ Sáu ngày 3 (xem nhật ký bên dưới).
PRTF(CopyTime("EURUSD", PERIOD_H1, D'2021.09.05', 10, times)); // 10 / ok
ArrayPrint(times);
/*
[0] 2021.09.03 14:00 2021.09.03 15:00 2021.09.03 16:00 2021.09.03 17:00 2021.09.03 18:00
[5] 2021.09.03 19:00 2021.09.03 20:00 2021.09.03 21:00 2021.09.03 22:00 2021.09.03 23:00
*/
2
3
4
5
6
Việc xuất mảng được thực hiện theo mặc định theo thứ tự thời gian (mặc dù các tham số hàm được đặt trong hệ tọa độ ngược: như trong chuỗi thời gian). Hãy thay đổi thứ tự lập chỉ mục trong mảng nhận và xuất lại.
PRTF(ArraySetAsSeries(times, true)); // true / ok
ArrayPrint(times);
/*
[0] 2021.09.03 23:00 2021.09.03 22:00 2021.09.03 21:00 2021.09.03 20:00 2021.09.03 19:00
[5] 2021.09.03 18:00 2021.09.03 17:00 2021.09.03 16:00 2021.09.03 15:00 2021.09.03 14:00
*/
2
3
4
5
6
Đối với các thí nghiệm tiếp theo, chúng ta sẽ khôi phục thứ tự thông thường.
PRTF(ArraySetAsSeries(times, false)); // true / ok
Bây giờ hãy yêu cầu một số lượng thanh không xác định giữa hai thời điểm (số lượng không xác định, vì có thể có ngày lễ trong phạm vi, ví dụ). Chúng ta sẽ làm điều này theo hai cách: trong trường hợp đầu tiên, chúng ta chỉ định phạm vi từ tương lai đến quá khứ, và trong trường hợp thứ hai, từ quá khứ đến tương lai. Kết quả trùng khớp.
// FROM TO
PRTF(CopyTime("EURUSD", PERIOD_H1, D'2021.09.06 03:00', D'2021.09.05 03:00', times));
ArrayPrint(times) // FROM TO
PRTF(CopyTime("EURUSD", PERIOD_H1, D'2021.09.05 03:00', D'2021.09.06 03:00', times));
ArrayPrint(times);
/*
CopyTime(EURUSD,PERIOD_H1,D'2021.09.06 03:00',D'2021.09.05 03:00',times)=4 / ok
2021.09.06 00:00 2021.09.06 01:00 2021.09.06 02:00 2021.09.06 03:00
CopyTime(EURUSD,PERIOD_H1,D'2021.09.05 03:00',D'2021.09.06 03:00',times)=4 / ok
2021.09.06 00:00 2021.09.06 01:00 2021.09.06 02:00 2021.09.06 03:00
*/
2
3
4
5
6
7
8
9
10
11
Bằng cách in các mảng, chúng ta có thể thấy rằng chúng giống hệt nhau. Hãy quay lại chế độ lập chỉ mục chuỗi thời gian và thảo luận thêm một điểm.
PRTF(ArraySetAsSeries(times, true)); // true / ok
ArrayPrint(times);
// 2021.09.06 03:00 2021.09.06 02:00 2021.09.06 01:00 2021.09.06 00:00
2
3
Mặc dù hai dấu thời gian cách nhau 24 giờ, điều này ngụ ý nhận được 25 phần tử trong mảng (nhớ rằng điểm bắt đầu và kết thúc được xử lý bao gồm cả hai), kết quả chỉ chứa 4 thanh. Sự thật là ngày 5 tháng 9 rơi vào Chủ nhật, và do đó, trong toàn bộ phạm vi, giao dịch chỉ được thực hiện trong các giờ sáng của ngày 6.
Ngoài ra, lưu ý rằng mảng nhận đã được tự động giảm kích thước từ 10 xuống 4 phần tử.
Cuối cùng, chúng ta sẽ yêu cầu 10 thanh, bắt đầu từ thanh thứ 100 (kết quả thu được sẽ phụ thuộc vào thời gian hiện tại và lịch sử có sẵn của bạn).
PRTF(CopyTime("EURUSD", PERIOD_H1, 100, 10, times)); // 10 / ok
ArrayPrint(times);
/*
[0] 2021.10.04 19:00 2021.10.04 18:00 2021.10.04 17:00 2021.10.04 16:00 2021.10.04 15:00
[5] 2021.10.04 14:00 2021.10.04 13:00 2021.10.04 12:00 2021.10.04 11:00 2021.10.04 10:00
*/
}
2
3
4
5
6
7
Do lập chỉ mục như trong chuỗi thời gian, mảng được hiển thị theo thứ tự thời gian ngược.