Kết hợp đầu ra vào cửa sổ chính và cửa sổ phụ
Hãy quay lại vấn đề hiển thị đồ họa từ một chỉ báo trong cửa sổ chính và trong cửa sổ phụ, vì chúng ta đã gặp phải vấn đề này khi phát triển ví dụ UseDemoAllSimple.mq5
. Các chỉ báo được thiết kế cho một cửa sổ riêng không phù hợp để hiển thị trên biểu đồ chính, và các chỉ báo cho cửa sổ chính không có cửa sổ phụ bổ sung. Có một số cách tiếp cận thay thế:
- Triển khai một chỉ báo cha cho một cửa sổ riêng và hiển thị các biểu đồ ở đó, đồng thời sử dụng nó trong cửa sổ chính để hiển thị dữ liệu dạng đối tượng đồ họa. Điều này không tốt, vì dữ liệu từ các đối tượng không thể được đọc giống như từ chuỗi thời gian, và nhiều đối tượng tiêu tốn thêm tài nguyên.
- Phát triển một bảng điều khiển ảo (lớp) của riêng bạn cho cửa sổ chính và, với tỷ lệ chính xác, thể hiện ở đó các chuỗi thời gian lẽ ra nên được hiển thị trong cửa sổ phụ.
- Sử dụng nhiều chỉ báo, ít nhất một cho cửa sổ chính và một cho cửa sổ phụ, và trao đổi dữ liệu giữa chúng qua bộ nhớ chia sẻ (yêu cầu DLL), tài nguyên hoặc cơ sở dữ liệu.
- Sao chép các phép tính (sử dụng mã nguồn chung) trong các chỉ báo cho cửa sổ chính và cửa sổ phụ.
Chúng ta sẽ trình bày một trong những giải pháp vượt ra ngoài một chương trình MQL đơn lẻ: chúng ta cần một chỉ báo bổ sung với thuộc tính indicator_separate_window
. Thực tế, chúng ta đã có nó vì chúng ta tạo phần tính toán của nó bằng cách yêu cầu một handle. Chúng ta chỉ cần tìm cách hiển thị nó trong một cửa sổ phụ riêng biệt.
Trong phiên bản mới (đầy đủ) của UseDemoAll.mq5
, chúng ta sẽ phân tích siêu dữ liệu của chỉ báo được yêu cầu tạo trong phần tử liệt kê tương ứng IndicatorType
. Hãy nhớ rằng, trong số các thông tin khác, cửa sổ làm việc của mỗi loại chỉ báo tích hợp sẵn được mã hóa ở đó. Khi một chỉ báo yêu cầu một cửa sổ riêng, chúng ta sẽ tạo một cửa sổ bằng cách sử dụng các hàm đặc biệt của MQL5, mà chúng ta chưa tìm hiểu.
Không có cách nào để lấy thông tin về cửa sổ làm việc cho các chỉ báo tùy chỉnh. Vì vậy, hãy thêm biến đầu vào IndicatorCustomSubwindow
, trong đó người dùng có thể chỉ định rằng cần một cửa sổ phụ.
input bool IndicatorCustomSubwindow = false; // Cửa sổ phụ cho chỉ báo tùy chỉnh
Trong OnInit
, chúng ta ẩn các bộ đệm dành cho cửa sổ phụ.
int OnInit()
{
...
const bool subwindow = (IND_WINDOW(IndicatorSelector) > 0)
|| (IndicatorSelector == iCustom_ && IndicatorCustomSubwindow);
for(int i = 0; i < BUF_NUM; ++i)
{
...
PlotIndexSetInteger(i, PLOT_DRAW_TYPE,
i < n && !subwindow ? DrawType : DRAW_NONE);
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
Sau khi thiết lập này, chúng ta sẽ phải sử dụng một vài hàm không chỉ áp dụng cho việc làm việc với chỉ báo mà còn với biểu đồ. Chúng ta sẽ nghiên cứu chúng chi tiết trong chương tương ứng, trong khi một tổng quan giới thiệu được trình bày trong phần trước.
Một trong những hàm, ChartIndicatorAdd
, cho phép bạn thêm chỉ báo được chỉ định bởi handle vào cửa sổ, không chỉ vào phần chính mà còn vào cửa sổ phụ. Chúng ta sẽ nói về định danh biểu đồ và đánh số cửa sổ trong chương về biểu đồ, và hiện tại chỉ cần biết rằng lời gọi hàm ChartIndicatorAdd
tiếp theo sẽ thêm một chỉ báo với handle
vào biểu đồ hiện tại, trong một cửa sổ phụ mới.
int handle = ... // lấy handle của chỉ báo, iCustom hoặc IndicatorCreate
// thiết lập biểu đồ hiện tại (0)
// |
// | thiết lập số cửa sổ thành số lượng cửa sổ hiện tại
// | |
// | | truyền mô tả
// | | |
// v v v
ChartIndicatorAdd( 0, (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL), handle);
2
3
4
5
6
7
8
9
10
Biết về khả năng này, chúng ta có thể nghĩ đến việc gọi ChartIndicatorAdd
và truyền cho nó handle của một chỉ báo phụ đã sẵn sàng.
Hàm thứ hai chúng ta cần là ChartIndicatorName
. Nó trả về tên ngắn của chỉ báo theo handle của nó. Tên này tương ứng với thuộc tính INDICATOR_SHORTNAME được đặt trong mã chỉ báo và có thể khác với tên tệp. Tên này sẽ cần thiết để dọn dẹp sau khi hoàn tất, tức là để xóa chỉ báo phụ và cửa sổ phụ của nó, sau khi xóa hoặc cấu hình lại chỉ báo cha.
string subTitle = "";
int OnInit()
{
...
if(subwindow)
{
// hiển thị một chỉ báo mới trong cửa sổ phụ
const int w = (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL);
ChartIndicatorAdd(0, w, Handle);
// lưu tên để xóa chỉ báo trong OnDeinit
subTitle = ChartIndicatorName(0, w, 0);
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Trong trình xử lý OnDeinit
, chúng ta sử dụng subTitle
đã lưu để gọi một hàm khác mà chúng ta sẽ nghiên cứu sau — ChartIndicatorDelete
. Nó xóa chỉ báo với tên được chỉ định trong đối số cuối cùng khỏi biểu đồ.
void OnDeinit(const int)
{
Print(__FUNCSIG__, (StringLen(subTitle) > 0 ? " deleting " + subTitle : ""));
if(StringLen(subTitle) > 0)
{
ChartIndicatorDelete(0, (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL) - 1,
subTitle);
}
}
2
3
4
5
6
7
8
9
Ở đây giả định rằng chỉ có chỉ báo của chúng ta hoạt động trên biểu đồ, và chỉ trong một phiên bản duy nhất. Trong trường hợp tổng quát hơn, tất cả các cửa sổ phụ nên được phân tích để xóa chính xác, nhưng điều này sẽ yêu cầu thêm một vài hàm từ những hàm sẽ được trình bày trong chương về biểu đồ, vì vậy chúng ta tạm thời giới hạn ở phiên bản đơn giản.
Nếu bây giờ chúng ta chạy UseDemoAll
và chọn một chỉ báo được đánh dấu bằng dấu sao (tức là chỉ báo yêu cầu cửa sổ phụ) từ danh sách, ví dụ như RSI, chúng ta sẽ thấy kết quả mong đợi: RSI trong một cửa sổ riêng.
RSI trong cửa sổ phụ được tạo bởi chỉ báo UseDemoAll