Giờ tiết kiệm ánh sáng ban ngày (cục bộ)
Để xác định liệu đồng hồ cục bộ có được chuyển sang giờ tiết kiệm ánh sáng ban ngày hay không, MQL5 cung cấp hàm TimeDaylightSavings
. Hàm này lấy cài đặt từ hệ điều hành của bạn.
Việc xác định giờ tiết kiệm ánh sáng ban ngày trên máy chủ không đơn giản như vậy. Để làm điều này, bạn cần thực hiện phân tích MQL5 về báo giá, các sự kiện trong lịch kinh tế, hoặc thời gian chuyển đổi/swap trong lịch sử giao dịch tài khoản. Trong ví dụ dưới đây, chúng ta sẽ trình bày một trong những phương pháp khả thi.
int TimeDaylightSavings()
Hàm này trả về hiệu chỉnh tính bằng giây nếu giờ tiết kiệm ánh sáng ban ngày đã được áp dụng. Thời gian mùa đông là tiêu chuẩn cho mỗi múi giờ, vì vậy hiệu chỉnh cho khoảng thời gian này là zero. Dưới dạng công thức có điều kiện, cách tính hiệu chỉnh có thể được viết như sau:
TimeDaylightSavings() = TimeLocal winter() - TimeLocal summer()
Ví dụ, nếu múi giờ tiêu chuẩn (winter
) bằng UTC+3 (tức là thời gian múi giờ sớm hơn UTC 3 giờ), thì khi chuyển sang giờ tiết kiệm ánh sáng ban ngày (summer
), chúng ta cộng thêm 1 giờ và được UTC+4. Khi đó, TimeDaylightSavings
sẽ trả về -3600.
Ví dụ về cách sử dụng hàm được đưa ra trong script TimeSummer.mq5
, script này cũng đề xuất một trong những cách thực nghiệm khả thi để xác định chế độ phù hợp trên máy chủ.
void OnStart()
{
PRTF(TimeLocal()); // thời gian cục bộ của terminal
PRTF(TimeCurrent()); // thời gian máy chủ cuối cùng được biết đến
PRTF(TimeTradeServer()); // thời gian máy chủ ước tính
PRTF(TimeGMT()); // thời gian GMT (tính toán từ cục bộ qua độ lệch múi giờ)
PRTF(TimeGMTOffset()); // độ lệch múi giờ so với GMT, tính bằng giây
PRTF(TimeDaylightSavings());// hiệu chỉnh cho giờ mùa hè tính bằng giây
...
}
2
3
4
5
6
7
8
9
10
Trước tiên, hãy hiển thị tất cả các loại thời gian và hiệu chỉnh của nó do MQL5 cung cấp (các hàm TimeGMT
và TimeGMTOffset
sẽ được trình bày trong phần tiếp theo về Thời gian Toàn cầu, nhưng ý nghĩa của chúng đã phần nào rõ ràng từ mô tả trước đó).
Script này được cho là chạy vào các ngày giao dịch. Các mục trong nhật ký sẽ tương ứng với cài đặt của máy tính của bạn và máy chủ của nhà môi giới.
TimeLocal()=2021.09.09 22:06:17 / ok
TimeCurrent()=2021.09.09 22:06:10 / ok
TimeTradeServer()=2021.09.09 22:06:17 / ok
TimeGMT()=2021.09.09 19:06:17 / ok
TimeGMTOffset()=-10800 / ok
TimeDaylightSavings()=0 / ok
2
3
4
5
6
Trong trường hợp này, múi giờ của khách hàng lệch 3 giờ so với GMT (UTC+3), không có hiệu chỉnh cho giờ tiết kiệm ánh sáng ban ngày.
Bây giờ hãy xem xét máy chủ. Dựa trên giá trị của hàm TimeCurrent
, chúng ta có thể xác định thời gian hiện tại của máy chủ, nhưng không phải múi giờ tiêu chuẩn của nó, vì thời gian này có thể liên quan đến việc chuyển sang giờ tiết kiệm ánh sáng ban ngày (MQL5 không cung cấp thông tin về việc nó có được sử dụng hay không và hiện tại có được kích hoạt hay không).
Để xác định múi giờ thực sự của máy chủ và giờ tiết kiệm ánh sáng ban ngày, chúng ta sẽ sử dụng thực tế rằng việc dịch chuyển thời gian máy chủ ảnh hưởng đến báo giá. Như hầu hết các phương pháp thực nghiệm để giải quyết vấn đề, phương pháp này có thể không cho kết quả hoàn toàn chính xác trong một số trường hợp. Nếu so sánh với các nguồn khác cho thấy sự không nhất quán, nên chọn một phương pháp khác.
Thị trường Forex mở cửa vào Chủ nhật lúc 22:00 UT (điều này tương ứng với việc bắt đầu giao dịch buổi sáng ở khu vực Châu Á - Thái Bình Dương) và đóng cửa vào thứ Sáu lúc 22:00 (kết thúc giao dịch ở Mỹ). Điều này có nghĩa là trên các máy chủ ở múi giờ UTC+2 (Đông Âu), các thanh đầu tiên sẽ xuất hiện chính xác vào 0 giờ 0 phút thứ Hai. Theo giờ Trung Âu, tương ứng với UTC+1, tuần giao dịch bắt đầu vào 23:00 Chủ nhật.
Bằng cách tính toán thống kê về độ dịch chuyển trong ngày của thanh H1 đầu tiên sau mỗi kỳ nghỉ cuối tuần, chúng ta sẽ có được ước tính về múi giờ của máy chủ. Tất nhiên, để làm điều này, tốt nhất nên sử dụng công cụ Forex có tính thanh khoản cao nhất, đó là EURUSD.
Nếu trong thống kê cho một khoảng thời gian hàng năm, hai độ dịch chuyển trong ngày tối đa được tìm thấy và chúng nằm cạnh nhau, điều này sẽ có nghĩa là nhà môi giới đang chuyển đổi sang giờ tiết kiệm ánh sáng ban ngày và ngược lại.
Lưu ý rằng khoảng thời gian mùa hè và mùa đông không bằng nhau. Vì vậy, khi chuyển sang giờ mùa hè vào đầu tháng Ba và quay lại giờ mùa đông vào đầu tháng Mười Một, chúng ta có khoảng 8 tháng giờ mùa hè. Điều này sẽ ảnh hưởng đến tỷ lệ các giá trị tối đa trong thống kê.
Khi có hai múi giờ, chúng ta có thể dễ dàng xác định xem múi giờ nào đang hoạt động vào lúc này và do đó tìm ra sự hiện diện hoặc vắng mặt của hiệu chỉnh cho giờ tiết kiệm ánh sáng ban ngày.
Khi chuyển đồng hồ sang giờ tiết kiệm ánh sáng ban ngày, múi giờ của nhà môi giới sẽ thay đổi từ UTC+2 sang UTC+3, điều này sẽ dịch chuyển thời gian bắt đầu tuần từ 22:00 sang 21:00. Điều này sẽ ảnh hưởng đến cấu trúc của các thanh H1: trên biểu đồ, chúng ta sẽ thấy ba thanh vào tối Chủ nhật thay vì hai.
Thay đổi giờ từ mùa đông (UTC+2) sang mùa hè (UTC+3) trên biểu đồ H1 EURUSD
Để thực hiện điều này, chúng ta có một hàm riêng biệt, ServerTimeZone
. Việc gọi hàm tích hợp CopyTime
chịu trách nhiệm lấy báo giá, hay chính xác hơn là dấu thời gian của thanh (chúng ta sẽ nghiên cứu hàm này trong phần về truy cập chuỗi thời gian).
ServerTime ServerTimeZone(const string symbol = NULL)
{
const int year = 365 * 24 * 60 * 60;
datetime array[];
if(PRTF(CopyTime(symbol, PERIOD_H1, TimeCurrent() - year, TimeCurrent(), array)) > 0)
{
// ở đây chúng ta nhận được khoảng 6000 thanh trong mảng
const int n = ArraySize(array);
PrintFormat("Got %d H1 bars, ~%d days", n, n / 24);
// (-V-) lặp qua các thanh H1
...
}
}
2
3
4
5
6
7
8
9
10
11
12
13
Hàm CopyTime
nhận công cụ làm việc, khung thời gian H1, và phạm vi ngày trong năm vừa qua làm tham số. Giá trị NULL thay vì công cụ có nghĩa là biểu tượng của biểu đồ hiện tại nơi script sẽ được đặt, vì vậy nên chọn cửa sổ có EURUSD. Hằng số PERIOD_H1 tương ứng với H1, như bạn có thể đoán. Chúng ta đã quen với hàm TimeCurrent
: nó sẽ trả về thời gian hiện tại, mới nhất được biết đến của máy chủ. Và nếu chúng ta trừ đi số giây trong một năm, được đặt vào biến year
, chúng ta sẽ nhận được ngày và giờ cách đây đúng một năm. Kết quả sẽ được đưa vào array
.
Để tính toán thống kê về việc tuần được mở bao nhiêu lần bằng một thanh ở một giờ cụ thể, chúng ta dành sẵn mảng hours[24]
. Việc tính toán sẽ được thực hiện trong một vòng lặp qua array
kết quả, tức là qua các thanh từ quá khứ đến hiện tại. Tại mỗi lần lặp, giờ mở cửa của tuần đang được xem xét sẽ được lưu trong biến current
. Khi vòng lặp kết thúc, múi giờ hiện tại của máy chủ sẽ vẫn còn trong current
, vì tuần hiện tại sẽ được xử lý cuối cùng.
// (-v-) lặp qua các thanh H1
int hours[24] = {};
int current = 0;
for(int i = 0; i < n; ++i)
{
// (-V-) xử lý thanh H1 thứ i
...
}
Print("Week opening hours stats:");
ArrayPrint(hours);
2
3
4
5
6
7
8
9
10
11
Bên trong vòng lặp ngày, chúng ta sẽ sử dụng lớp datetime
từ tệp tiêu đề MQL5Book/DateTime.mqh
(xem Ngày và giờ).
// (-v-) xử lý thanh H1 thứ i
// tìm ngày trong tuần của thanh
const ENUM_DAY_OF_WEEK weekday = TimeDayOfWeek(array[i]);
// bỏ qua tất cả các ngày trừ Chủ nhật và Thứ Hai
if(weekday > MONDAY) continue;
// phân tích thanh H1 đầu tiên của tuần giao dịch tiếp theo
// tìm giờ của thanh đầu tiên sau cuối tuần
current = _TimeHour();
// tính toán thống kê giờ mở cửa
hours[current]++;
// bỏ qua 2 ngày tiếp theo
// (vì thống kê cho đầu tuần này đã được cập nhật)
i += 48;
2
3
4
5
6
7
8
9
10
11
12
13
14
Thuật toán được đề xuất không tối ưu, nhưng nó không yêu cầu hiểu chi tiết kỹ thuật về tổ chức chuỗi thời gian, điều mà chúng ta chưa biết.
Một số tuần không được định dạng (bắt đầu sau các ngày lễ). Nếu tình huống này xảy ra trong tuần cuối cùng, biến current
sẽ chứa một độ lệch bất thường. Điều này có thể được xác minh bằng thống kê: đối với giờ kết quả, sẽ có rất ít lần ghi nhận "mở cửa" của tuần. Trong script thử nghiệm, trong trường hợp này, chỉ một thông báo được hiển thị trong nhật ký. Trong thực tế, bạn nên làm rõ giờ mở cửa tiêu chuẩn cho một đến hai tuần trước đó.
// (-V-) lặp qua các thanh H1
...
if(hours[current] <= 52 / 4)
{
// TODO: kiểm tra cho các tuần trước
Print("Extraordinary week detected");
}
2
3
4
5
6
7
Nếu nhà môi giới không chuyển sang giờ tiết kiệm ánh sáng ban ngày, thống kê sẽ có một giá trị tối đa, bao gồm tất cả hoặc gần như tất cả các tuần. Nếu nhà môi giới thực hiện thay đổi múi giờ, sẽ có hai đỉnh trong thống kê.
// tìm độ dịch chuyển thời gian thường xuyên nhất
int max = ArrayMaximum(hours);
// sau đó kiểm tra xem có độ dịch chuyển thường xuyên khác không
hours[max] = 0;
int sub = ArrayMaximum(hours);
2
3
4
5
Chúng ta cần xác định mức độ quan trọng của cực trị thứ hai (tức là khác với các ngày lễ ngẫu nhiên có thể làm dịch chuyển thời gian bắt đầu tuần). Để làm điều này, chúng ta đánh giá thống kê cho một phần tư năm (52 tuần / 4). Nếu vượt quá giới hạn này, nhà môi giới hỗ trợ giờ tiết kiệm ánh sáng ban ngày.
int DST = 0;
if(hours[sub] > 52 / 4)
{
// cơ bản, DST được hỗ trợ
if(current == max || current == sub)
{
if(current == MathMin(max, sub))
DST = fabs(max - sub); // DST đang được bật hiện tại
}
}
2
3
4
5
6
7
8
9
10
Nếu độ lệch của việc mở tuần hiện tại (trong biến current
) trùng với một trong hai cực trị chính, thì tuần hiện tại đã mở bình thường, và nó có thể được sử dụng để đưa ra kết luận về múi giờ (điều kiện bảo vệ này là cần thiết vì chúng ta không có hiệu chỉnh cho các tuần không tiêu chuẩn và chỉ phát ra cảnh báo thay vì vậy).
Bây giờ mọi thứ đã sẵn sàng để tạo phản hồi của hàm của chúng ta: múi giờ máy chủ và dấu hiệu của giờ tiết kiệm ánh sáng ban ngày đã được bật.
current += 2 + DST; // +2 để lấy độ lệch từ UTC
current %= 24;
// múi giờ luôn nằm trong khoảng [UTC-12, UTC+12]
if(current > 12) current = current - 24;
2
3
4
Vì chúng ta có hai đặc điểm để trả về từ hàm (current
và DST
), và ngoài ra, chúng ta có thể cho biết mã được gọi liệu nhà môi giới có sử dụng giờ tiết kiệm ánh sáng ban ngày ngay từ đầu hay không (ngay cả khi hiện tại là mùa đông), việc khai báo một cấu trúc đặc biệt ServerTime
với tất cả các trường cần thiết là hợp lý.
struct ServerTime
{
int offsetGMT; // múi giờ tính bằng giây so với UTC/GMT
int offsetDST; // hiệu chỉnh DST tính bằng giây (bao gồm trong offsetGMT)
bool supportDST; // hiệu chỉnh DST được phát hiện trong báo giá về nguyên tắc
string description; // mô tả kết quả
};
2
3
4
5
6
7
Sau đó, trong hàm ServerTimeZone
, chúng ta có thể điền và trả về cấu trúc như vậy làm kết quả của công việc.
ServerTime st = {};
st.description = StringFormat("Server time offset: UTC%+d, including DST%+d", current, DST);
st.offsetGMT = -current * 3600;
st.offsetDST = -DST * 3600;
return st;
2
3
4
5
Nếu vì lý do nào đó hàm không thể lấy được báo giá, chúng ta sẽ trả về một cấu trúc rỗng.
ServerTime ServerTimeZone(const string symbol = NULL)
{
const int year = 365 * 24 * 60 * 60;
datetime array[];
if(PRTF(CopyTime(symbol, PERIOD_H1, TimeCurrent() - year, TimeCurrent(), array)) > 0)
{
...
return st;
}
ServerTime empty = {-INT_MAX, -INT_MAX, false};
return empty;
}
2
3
4
5
6
7
8
9
10
11
12
Hãy kiểm tra hàm mới trong thực tế, vì vậy trong OnStart
, chúng ta thêm các lệnh sau:
...
ServerTime st = ServerTimeZone();
Print(st.description);
Print("ServerGMTOffset: ", st.offsetGMT);
Print("ServerTimeDaylightSavings: ", st.offsetDST);
}
2
3
4
5
6
Hãy xem xét các kết quả có thể xảy ra.
CopyTime(symbol,PERIOD_H1,TimeCurrent()-year,TimeCurrent(),array)=6207 / ok
Got 6207 H1 bars, ~258 days
Week opening hours stats:
52 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Server time offset: UTC+2, including DST+0
ServerGMTOffset: -7200
ServerTimeDaylightSavings: 0
2
3
4
5
6
7
Theo thống kê thu thập được của các thanh H1, tuần của nhà môi giới này mở cửa nghiêm ngặt vào 00:00 thứ Hai. Do đó, múi giờ thực tế bằng UTC+2, và không có hiệu chỉnh cho giờ mùa hè, tức là thời gian máy chủ phải khớp với EET (UTC+2). Tuy nhiên, trong thực tế, như chúng ta đã thấy ở phần đầu của nhật ký, thời gian trên máy chủ khác GMT 3 giờ.
Ở đây chúng ta có thể giả định rằng chúng ta đã gặp một máy chủ hoạt động quanh năm theo giờ mùa hè. Trong trường hợp đó, hàm ServerTimeZone
sẽ không thể phân biệt hiệu chỉnh từ giờ bổ sung trong "múi giờ": kết quả là, chế độ DST sẽ bằng zero, và thời gian GMT tính toán từ báo giá máy chủ sẽ dịch sang phải một giờ so với thực tế. Hoặc giả định ban đầu của chúng ta rằng báo giá bắt đầu đến vào 22:00 Chủ nhật không tương ứng với chế độ hoạt động của máy chủ này. Những điểm như vậy nên được làm rõ với dịch vụ hỗ trợ của nhà môi giới.