Chế độ hiển thị biểu đồ
Bốn thuộc tính từ liệt kê ENUM_CHART_PROPERTY_INTEGER
mô tả các chế độ hiển thị biểu đồ. Tất cả các thuộc tính này đều có thể được đọc thông qua ChartGetInteger
, và ghi thông qua ChartSetInteger
, cho phép bạn thay đổi giao diện của biểu đồ.
Định danh | Mô tả | Kiểu giá trị |
---|---|---|
CHART_MODE | Loại biểu đồ (nến, thanh, hoặc đường) | ENUM_CHART_MODE |
CHART_FOREGROUND | Biểu đồ giá ở phía trước | bool |
CHART_SHIFT | Chế độ thụt lề biểu đồ giá từ cạnh phải | bool |
CHART_AUTOSCROLL | Tự động cuộn đến cạnh phải của biểu đồ | bool |
Có một liệt kê đặc biệt ENUM_CHART_MODE
cho chế độ CHART_MODE
trong MQL5. Các phần tử của nó được hiển thị trong bảng sau.
Định danh | Mô tả | Giá trị |
---|---|---|
CHART_BARS | Hiển thị dưới dạng thanh | 0 |
CHART_CANDLES | Hiển thị dưới dạng nến Nhật Bản | 1 |
CHART_LINE | Hiển thị dưới dạng đường vẽ tại giá đóng cửa | 2 |
Hãy triển khai script ChartMode.mq5
, script này sẽ theo dõi trạng thái của các chế độ và in thông báo vào nhật ký khi phát hiện thay đổi. Vì các thuật toán xử lý thuộc tính mang tính chất chung, chúng ta sẽ đặt chúng trong một tệp tiêu đề riêng ChartModeMonitor.mqh
, sau đó sẽ kết nối với các bài kiểm tra khác nhau.
Hãy đặt nền tảng trong một lớp trừu tượng ChartModeMonitorInterface
: nó cung cấp các phương thức get- và set- được nạp chồng cho tất cả các kiểu. Các lớp dẫn xuất sẽ phải kiểm tra trực tiếp các thuộc tính ở mức độ cần thiết bằng cách ghi đè phương thức ảo snapshot
.
class ChartModeMonitorInterface
{
public:
long get(const ENUM_CHART_PROPERTY_INTEGER property, const int window = 0)
{
return ChartGetInteger(0, property, window);
}
double get(const ENUM_CHART_PROPERTY_DOUBLE property, const int window = 0)
{
return ChartGetDouble(0, property, window);
}
string get(const ENUM_CHART_PROPERTY_STRING property)
{
return ChartGetString(0, property);
}
bool set(const ENUM_CHART_PROPERTY_INTEGER property, const long value, const int window = 0)
{
return ChartSetInteger(0, property, window, value);
}
bool set(const ENUM_CHART_PROPERTY_DOUBLE property, const double value)
{
return ChartSetDouble(0, property, value);
}
bool set(const ENUM_CHART_PROPERTY_STRING property, const string value)
{
return ChartSetString(0, property, value);
}
virtual void snapshot() = 0;
virtual void print() { };
virtual void backup() { }
virtual void restore() { }
};
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
Lớp này cũng có các phương thức dự phòng: print
, ví dụ, để xuất ra nhật ký, backup
để lưu trạng thái hiện tại, và restore
để khôi phục nó. Chúng được khai báo không phải là trừu tượng, mà với một triển khai rỗng, vì chúng là tùy chọn.
Có ý nghĩa khi định nghĩa các lớp nhất định cho các thuộc tính của các kiểu khác nhau dưới dạng một mẫu duy nhất kế thừa từ ChartModeMonitorInterface
và chấp nhận các kiểu giá trị tham số (T) và liệt kê (E). Ví dụ, đối với các thuộc tính số nguyên, bạn sẽ cần đặt T=long
và E=ENUM_CHART_PROPERTY_INTEGER
.
Đối tượng chứa mảng data
để lưu trữ các cặp [key,value] với tất cả các thuộc tính được yêu cầu. Nó có kiểu chung MapArray<K,V>
, mà chúng ta đã giới thiệu trước đó cho chỉ báo IndUnityPercent
trong chương Chỉ báo đa tiền tệ và đa khung thời gian. Đặc điểm của nó nằm ở chỗ ngoài việc truy cập thông thường vào các phần tử mảng bằng số, có thể sử dụng địa chỉ bằng khóa.
Để điền vào mảng, một mảng các số nguyên được truyền vào hàm tạo, trong khi các số nguyên được kiểm tra trước tiên xem có phù hợp với các định danh của liệt kê E đã cho hay không bằng phương thức detect
. Tất cả các thuộc tính đúng ngay lập tức được đọc thông qua lệnh gọi get
, và các giá trị kết quả được lưu trong bản đồ cùng với định danh của chúng.
#include <MQL5Book/MapArray.mqh>
template<typename T,typename E>
class ChartModeMonitorBase: public ChartModeMonitorInterface
{
protected:
MapArray<E,T> data; // array-map of pairs [property, value]
// the method checks if the passed constant is an enumeration element,
// and if it is, then add it to the map array
bool detect(const int v)
{
ResetLastError();
EnumToString((E)v); // resulting string is not used
if(_LastError == 0) // it only matters if there is an error or not
{
data.put((E)v, get((E)v));
return true;
}
return false;
}
public:
ChartModeMonitorBase(int &flags[])
{
for(int i = 0; i < ArraySize(flags); ++i)
{
detect(flags[i]);
}
}
virtual void snapshot() override
{
MapArray<E,T> temp;
// collect the current state of all properties
for(int i = 0; i < data.getSize(); ++i)
{
temp.put(data.getKey(i), get(data.getKey(i)));
}
// compare with previous state, display differences
for(int i = 0; i < data.getSize(); ++i)
{
if(data[i] != temp[i])
{
Print(EnumToString(data.getKey(i)), " ", data[i], " -> ", temp[i]);
}
}
// save for next comparison
data = temp;
}
...
};
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
Phương thức snapshot
lặp qua tất cả các phần tử của mảng và yêu cầu giá trị cho mỗi thuộc tính. Vì chúng ta muốn phát hiện thay đổi, dữ liệu mới trước tiên được lưu trong một mảng bản đồ tạm thời temp
. Sau đó, các mảng data
và temp
được so sánh từng phần tử, và với mỗi sự khác biệt, một thông báo được hiển thị với tên của thuộc tính, giá trị cũ và mới của nó. Ví dụ đơn giản hóa này chỉ sử dụng nhật ký. Tuy nhiên, nếu cần, chương trình có thể gọi một số hàm ứng dụng để điều chỉnh hành vi theo môi trường.
Các phương thức print
, backup
, và restore
được triển khai đơn giản nhất có thể.
template<typename T,typename E>
class ChartModeMonitorBase: public ChartModeMonitorInterface
{
protected:
...
MapArray<E,T> store; // backup
public:
...
virtual void print() override
{
data.print();
}
virtual void backup() override
{
store = data;
}
virtual void restore() override
{
data = store;
// restore chart properties
for(int i = 0; i < data.getSize(); ++i)
{
set(data.getKey(i), data[i]);
}
}
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
Sự kết hợp của các phương thức backup/restore
cho phép bạn lưu trạng thái của biểu đồ trước khi bắt đầu thử nghiệm với nó, và sau khi hoàn thành script thử nghiệm, khôi phục mọi thứ như ban đầu.
Cuối cùng, lớp cuối cùng trong tệp ChartModeMonitor.mqh
là ChartModeMonitor
. Nó kết hợp ba thể hiện của ChartModeMonitorBase
, được tạo ra cho các kết hợp kiểu thuộc tính có sẵn. Chúng có một mảng m
gồm các con trỏ đến giao diện cơ sở ChartModeMonitorInterface
. Bản thân lớp này cũng được dẫn xuất từ nó.
#include <MQL5Book/AutoPtr.mqh>
#define CALL_ALL(A,M) for(int i = 0, size = ArraySize(A); i < size; ++i) A[i][].M
class ChartModeMonitor: public ChartModeMonitorInterface
{
AutoPtr<ChartModeMonitorInterface> m[3];
public:
ChartModeMonitor(int &flags[])
{
m[0] = new ChartModeMonitorBase<long,ENUM_CHART_PROPERTY_INTEGER>(flags);
m[1] = new ChartModeMonitorBase<double,ENUM_CHART_PROPERTY_DOUBLE>(flags);
m[2] = new ChartModeMonitorBase<string,ENUM_CHART_PROPERTY_STRING>(flags);
}
virtual void snapshot() override
{
CALL_ALL(m, snapshot());
}
virtual void print() override
{
CALL_ALL(m, print());
}
virtual void backup() override
{
CALL_ALL(m, backup());
}
virtual void restore() override
{
CALL_ALL(m, restore());
}
};
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
Để đơn giản hóa mã, macro CALL_ALL
được sử dụng ở đây, nó gọi phương thức được chỉ định cho tất cả các đối tượng từ mảng, và thực hiện điều này có tính đến toán tử nạp chồng []
trong lớp AutoPtr
(nó được sử dụng để giải tham chiếu một con trỏ thông minh và lấy con trỏ trực tiếp đến đối tượng "được bảo vệ").
Hàm hủy thường chịu trách nhiệm giải phóng các đối tượng, nhưng trong trường hợp này, quyết định sử dụng mảng AutoPtr
(lớp này đã được thảo luận trong phần Mẫu kiểu đối tượng). Điều này đảm bảo việc xóa tự động các đối tượng động khi mảng m
được giải phóng bình thường.
Phiên bản đầy đủ hơn của trình giám sát với hỗ trợ số cửa sổ phụ được cung cấp trong tệp ChartModeMonitorFull.mqh
.
Dựa trên lớp ChartModeMonitor
, bạn có thể dễ dàng triển khai script dự định ChartMode.mq5
. Nhiệm vụ của nó là kiểm tra trạng thái của một tập hợp thuộc tính đã cho mỗi nửa giây. Bây giờ chúng ta đang sử dụng một vòng lặp vô hạn và Sleep
ở đây, nhưng sắp tới chúng ta sẽ học cách phản ứng với các sự kiện trên biểu đồ theo cách khác: nhờ thông báo từ terminal.
#include <MQL5Book/ChartModeMonitor.mqh>
void OnStart()
{
int flags[] =
{
CHART_MODE, CHART_FOREGROUND, CHART_SHIFT, CHART_AUTOSCROLL
};
ChartModeMonitor m(flags);
Print("Initial state:");
m.print();
m.backup();
while(!IsStopped())
{
m.snapshot();
Sleep(500);
}
m.restore();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Chạy script trên bất kỳ biểu đồ nào và thử thay đổi chế độ bằng các nút công cụ. Bằng cách này, bạn có thể truy cập tất cả các phần tử ngoại trừ CHART_FOREGROUND
, có thể được chuyển đổi từ hộp thoại thuộc tính (tab Common
, cờ Chart on top
).
Các nút thanh công cụ để chuyển đổi chế độ biểu đồ
Ví dụ, nhật ký sau được tạo ra bằng cách chuyển đổi hiển thị từ nến sang thanh, từ thanh sang đường, và quay lại nến, sau đó bật thụt lề và tự động cuộn về đầu.
Initial state:
[key] [value]
[0] 0 1
[1] 1 0
[2] 2 0
[3] 4 0
CHART_MODE 1 -> 0
CHART_MODE 0 -> 2
CHART_MODE 2 -> 1
CHART_SHIFT 0 -> 1
CHART_AUTOSCROLL 0 -> 1
2
3
4
5
6
7
8
9
10
11
Một ví dụ thực tế hơn về việc sử dụng thuộc tính CHART_MODE
là phiên bản cải tiến của chỉ báo IndSubChart.mq5
(chúng ta đã thảo luận phiên bản đơn giản hóa của nó IndSubChartSimple.mq5
trong phần Chỉ báo đa tiền tệ và đa khung thời gian). Chỉ báo này được thiết kế để hiển thị báo giá của một biểu tượng bên thứ ba trong một cửa sổ phụ, và trước đây chúng ta phải yêu cầu người dùng chọn phương thức hiển thị (nến, thanh, hoặc đường) thông qua một tham số đầu vào. Bây giờ tham số này không còn cần thiết nữa vì chúng ta có thể tự động chuyển chỉ báo sang chế độ được sử dụng trong cửa sổ chính.
Chế độ hiện tại được lưu trong biến toàn cục mode
và được gán lần đầu trong quá trình khởi tạo.
ENUM_CHART_MODE mode = 0;
int OnInit()
{
...
mode = (ENUM_CHART_MODE)ChartGetInteger(0, CHART_MODE);
...
}
2
3
4
5
6
7
8
Việc phát hiện chế độ mới tốt nhất nên được thực hiện trong một trình xử lý sự kiện được thiết kế đặc biệt OnChartEvent
, mà chúng ta sẽ nghiên cứu trong một chương riêng. Ở giai đoạn này, điều quan trọng là biết rằng với bất kỳ thay đổi nào trên biểu đồ, chương trình MQL có thể nhận thông báo từ terminal nếu mã mô tả một hàm với nguyên mẫu được xác định trước này (tên và danh sách tham số). Đặc biệt, tham số đầu tiên của nó chứa định danh sự kiện mô tả ý nghĩa của nó. Chúng ta vẫn quan tâm đến chính biểu đồ, và vì vậy chúng ta kiểm tra xem eventId
có bằng CHARTEVENT_CHART_CHANGE
không. Điều này là cần thiết vì trình xử lý cũng có khả năng theo dõi các đối tượng đồ họa, bàn phím, chuột, và các thông điệp tùy ý của người dùng.
void OnChartEvent(const int eventId,
// parameters not used here
const long &, const double &, const string &)
{
if(eventId == CHARTEVENT_CHART_CHANGE)
{
const ENUM_CHART_MODE newmode = (ENUM_CHART_MODE)ChartGetInteger(0, CHART_MODE);
if(mode != newmode)
{
const ENUM_CHART_MODE oldmode = mode;
mode = newmode;
// change buffer bindings and rendering type on the go
InitPlot(0, InitBuffers(mode), Mode2Style(mode));
// TODO: we will auto-adjust colors later
// SetPlotColors(0, mode);
if(oldmode == CHART_LINE || newmode == CHART_LINE)
{
// switching to or from CHART_LINE mode requires updating the entire chart,
// because the number of buffers changes
Print("Refresh");
ChartSetSymbolPeriod(0, _Symbol, _Period);
}
else
{
// when switching between candles and bars, it is enough
// just redraw the chart in a new manner,
// because data doesn't change (previous 4 buffers with values)
Print("Redraw");
ChartRedraw();
}
}
}
}
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
Bạn có thể tự kiểm tra chỉ báo mới bằng cách chạy nó trên biểu đồ và chuyển đổi các phương thức vẽ.
Đây không phải là tất cả các cải tiến được thực hiện trong IndSubChart.mq5
. Một chút sau, trong phần về màu sắc biểu đồ, chúng ta sẽ cho thấy sự điều chỉnh tự động của đồ họa theo bảng màu của biểu đồ.