Xóa các phiên bản chỉ báo: IndicatorRelease
Như đã đề cập trong phần giới thiệu của chương này, terminal duy trì một bộ đếm tham chiếu cho mỗi chỉ báo được tạo và giữ nó hoạt động miễn là ít nhất một chương trình MQL hoặc biểu đồ sử dụng nó. Trong một chương trình MQL, dấu hiệu của việc cần một chỉ báo là một handle hợp lệ. Thông thường, chúng ta yêu cầu một handle trong quá trình khởi tạo và sử dụng nó trong các thuật toán cho đến khi chương trình kết thúc.
Tại thời điểm chương trình được dỡ bỏ, tất cả các handle duy nhất đã tạo sẽ tự động được giải phóng, tức là bộ đếm của chúng giảm đi 1 (và nếu chúng đạt zero, các chỉ báo đó cũng sẽ được dỡ khỏi bộ nhớ). Do đó, không cần phải giải phóng handle một cách rõ ràng.
Tuy nhiên, có những tình huống mà một chỉ báo phụ trở nên không cần thiết trong quá trình hoạt động của chương trình. Khi đó, chỉ báo vô dụng tiếp tục tiêu tốn tài nguyên. Vì vậy, bạn phải giải phóng handle một cách rõ ràng bằng IndicatorRelease
.
bool IndicatorRelease(int handle)
Hàm này xóa handle chỉ báo được chỉ định và dỡ bỏ chính chỉ báo đó nếu không còn ai khác sử dụng nó. Việc dỡ bỏ xảy ra với một chút chậm trễ.
Hàm trả về một chỉ báo thành công (true
) hoặc lỗi (false
).
Sau khi gọi IndicatorRelease
, handle được truyền vào nó trở nên không hợp lệ, mặc dù bản thân biến vẫn giữ giá trị trước đó. Việc cố gắng sử dụng handle như vậy trong các hàm chỉ báo khác như CopyBuffer
sẽ thất bại với lỗi 4807 (ERR_INDICATOR_WRONG_HANDLE). Để tránh nhầm lẫn, nên gán giá trị INVALID_HANDLE cho biến tương ứng ngay sau khi handle được giải phóng.
Tuy nhiên, nếu sau đó chương trình yêu cầu một handle cho một chỉ báo mới, handle đó rất có thể sẽ có cùng giá trị với handle đã giải phóng trước đó nhưng giờ đây sẽ liên kết với dữ liệu của chỉ báo mới.
Khi làm việc trong trình kiểm tra chiến lược, hàm
IndicatorRelease
không được thực hiện.
Để minh họa việc áp dụng IndicatorRelease
, hãy chuẩn bị một phiên bản đặc biệt của UseDemoAllLoop.mq5
, sẽ định kỳ tạo lại một chỉ báo phụ trong một chu kỳ từ danh sách, chỉ bao gồm các chỉ báo cho cửa sổ chính (để rõ ràng).
IndicatorType MainLoop[] =
{
iCustom_,
iAlligator_jawP_jawS_teethP_teethS_lipsP_lipsS_method_price,
iAMA_period_fast_slow_shift_price,
iBands_period_shift_deviation_price,
iDEMA_period_shift_price,
iEnvelopes_period_shift_method_price_deviation,
iFractals_,
iFrAMA_period_shift_price,
iIchimoku_tenkan_kijun_senkou,
iMA_period_shift_method_price,
iSAR_step_maximum,
iTEMA_period_shift_price,
iVIDyA_momentum_smooth_shift_price,
};
const int N = ArraySize(MainLoop);
int Cursor = 0; // vị trí hiện tại trong mảng MainLoop
const string IndicatorCustom = "LifeCycle";
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Phần tử đầu tiên của mảng chứa một chỉ báo tùy chỉnh như một ngoại lệ, LifeCycle
, từ phần Tính năng khởi động và dừng các chương trình của các loại khác nhau. Mặc dù chỉ báo này không hiển thị bất kỳ đường nào, nó phù hợp ở đây vì nó hiển thị thông báo trong nhật ký khi các trình xử lý OnInit/OnDeinit
của nó được gọi, cho phép bạn theo dõi vòng đời của nó. Vòng đời của các chỉ báo khác cũng tương tự.
Trong các biến đầu vào, chúng ta sẽ chỉ giữ lại các cài đặt hiển thị. Đầu ra mặc định của nhãn DRAW_ARROW là tối ưu để hiển thị các loại chỉ báo khác nhau.
input ENUM_DRAW_TYPE DrawType = DRAW_ARROW; // Kiểu vẽ
input int DrawLineWidth = 1; // Độ rộng đường vẽ
2
Để tái tạo các chỉ báo "trên đường đi", hãy chạy bộ đếm thời gian 5 giây trong OnInit
, và toàn bộ khởi tạo trước đó (với một số sửa đổi được mô tả dưới đây) sẽ được chuyển sang trình xử lý OnTimer
.
int OnInit()
{
Comment("Wait 5 seconds to start looping through indicator set");
EventSetTimer(5);
return INIT_SUCCEEDED;
}
IndicatorType IndicatorSelector; // loại chỉ báo hiện được chọn
void OnTimer()
{
if(Handle != INVALID_HANDLE && ClearHandles)
{
IndicatorRelease(Handle);
/*
// handle vẫn là 10, nhưng không còn hợp lệ
// nếu chúng ta bỏ ghi chú đoạn mã này, chúng ta sẽ nhận được lỗi sau
double data[1];
const int n = CopyBuffer(Handle, 0, 0, 1, data);
Print("Handle=", Handle, " CopyBuffer=", n, " Error=", _LastError);
// Handle=10 CopyBuffer=-1 Error=4807 (ERR_INDICATOR_WRONG_HANDLE)
*/
}
IndicatorSelector = MainLoop[Cursor];
Cursor = ++Cursor % N;
// tạo một handle với các tham số mặc định
// (vì chúng ta truyền một chuỗi rỗng trong đối số thứ ba của hàm tạo)
AutoIndicator indicator(IndicatorSelector,
(IndicatorSelector == iCustom_ ? IndicatorCustom : ""), "");
Handle = indicator.getHandle();
if(Handle == INVALID_HANDLE)
{
Print(StringFormat("Can't create indicator: %s",
_LastError ? E2S(_LastError) : "The name or number of parameters is incorrect"));
}
else
{
Print("Handle=", Handle);
}
buffers.empty(); // xóa bộ đệm vì một chỉ báo mới sẽ được hiển thị
ChartSetSymbolPeriod(0, NULL, 0); // yêu cầu vẽ lại hoàn toàn
...
// thiết lập thêm các biểu đồ - tương tự như trước đó
...
Comment("DemoAll: ", (IndicatorSelector == iCustom_ ? IndicatorCustom : s),
"(default-params)");
}
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
Sự khác biệt chính là loại chỉ báo hiện được tạo, IndicatorSelector
, giờ đây không do người dùng đặt mà được chọn tuần tự từ mảng MainLoop
tại chỉ số Cursor
. Mỗi lần bộ đếm thời gian được gọi, chỉ số này tăng theo chu kỳ, tức là khi đến cuối mảng, chúng ta nhảy về đầu mảng.
Đối với tất cả các chỉ báo, dòng tham số trống. Điều này được thực hiện để thống nhất việc khởi tạo của chúng. Kết quả là, mỗi chỉ báo sẽ được tạo với các giá trị mặc định của nó.
Ở đầu trình xử lý OnTimer
, chúng ta gọi IndicatorRelease
cho handle trước đó. Tuy nhiên, chúng ta đã cung cấp một biến đầu vào ClearHandles
để tắt nhánh toán tử if
đã cho và xem điều gì xảy ra nếu bạn không dọn dẹp các handle.
input bool ClearHandles = true;
Mặc định, ClearHandles
bằng true
, tức là các chỉ báo sẽ được xóa như kỳ vọng.
Cuối cùng, một cài đặt bổ sung khác là các dòng với việc xóa bộ đệm và yêu cầu vẽ lại hoàn toàn biểu đồ. Cả hai đều cần thiết, vì chúng ta đã thay thế chỉ báo phụ cung cấp dữ liệu hiển thị.
Trình xử lý OnCalculate
không thay đổi.
Hãy chạy UseDemoAllLoop
với cài đặt mặc định. Các mục sau sẽ xuất hiện trong nhật ký (chỉ hiển thị phần đầu):
UseDemoAllLoop (EURUSD,H1) Initializing LifeCycle() EURUSD, PERIOD_H1
UseDemoAllLoop (EURUSD,H1) Handle=10
LifeCycle (EURUSD,H1) Loader::Loader()
LifeCycle (EURUSD,H1) void OnInit() 0 DEINIT_REASON_PROGRAM
UseDemoAllLoop (EURUSD,H1) Initializing iAlligator_jawP_jawS_teethP_teethS_lipsP_lipsS_method_price() EURUSD, PERIOD_H1
UseDemoAllLoop (EURUSD,H1) iAlligator_jawP_jawS_teethP_teethS_lipsP_lipsS_method_price requires 8 parameters, 0 given
UseDemoAllLoop (EURUSD,H1) Handle=10
LifeCycle (EURUSD,H1) void OnDeinit(const int) DEINIT_REASON_REMOVE
LifeCycle (EURUSD,H1) Loader::~Loader()
UseDemoAllLoop (EURUSD,H1) Initializing iAMA_period_fast_slow_shift_price() EURUSD, PERIOD_H1
UseDemoAllLoop (EURUSD,H1) iAMA_period_fast_slow_shift_price requires 5 parameters, 0 given
UseDemoAllLoop (EURUSD,H1) Handle=10
UseDemoAllLoop (EURUSD,H1) Initializing iBands_period_shift_deviation_price() EURUSD, PERIOD_H1
UseDemoAllLoop (EURUSD,H1) iBands_period_shift_deviation_price requires 4 parameters, 0 given
UseDemoAllLoop (EURUSD,H1) Handle=10
...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Lưu ý rằng chúng ta nhận được cùng "số" handle (10) mỗi lần vì chúng ta giải phóng nó trước khi tạo một handle mới.
Điều quan trọng cũng là chỉ báo LifeCycle
được dỡ bỏ ngay sau khi chúng ta giải phóng nó (giả sử nó không được thêm vào cùng biểu đồ bởi chính nó, vì khi đó bộ đếm tham chiếu của nó sẽ không được đặt lại về zero).
Hình ảnh dưới đây cho thấy khoảnh khắc khi chỉ báo của chúng ta hiển thị dữ liệu Alligator.
UseDemoAllLoop trong bước demo Alligator
Nếu bạn thay đổi giá trị của ClearHandles
thành false
, chúng ta sẽ thấy một bức tranh hoàn toàn khác trong nhật ký. Số handle giờ đây sẽ liên tục tăng, cho thấy các chỉ báo vẫn còn trong terminal và tiếp tục hoạt động, tiêu tốn tài nguyên một cách vô ích. Đặc biệt, không có thông báo dỡ bỏ nào được nhận từ chỉ báo LifeCycle
.
UseDemoAllLoop (EURUSD,H1) Initializing LifeCycle() EURUSD, PERIOD_H1
UseDemoAllLoop (EURUSD,H1) Handle=10
LifeCycle (EURUSD,H1) Loader::Loader()
LifeCycle (EURUSD,H1) void OnInit() 0 DEINIT_REASON_PROGRAM
UseDemoAllLoop (EURUSD,H1) Initializing iAlligator_jawP_jawS_teethP_teethS_lipsP_lipsS_method_price() EURUSD, PERIOD_H1
UseDemoAllLoop (EURUSD,H1) iAlligator_jawP_jawS_teethP_teethS_lipsP_lipsS_method_price requires 8 parameters, 0 given
UseDemoAllLoop (EURUSD,H1) Handle=11
UseDemoAllLoop (EURUSD,H1) Initializing iAMA_period_fast_slow_shift_price() EURUSD, PERIOD_H1
UseDemoAllLoop (EURUSD,H1) iAMA_period_fast_slow_shift_price requires 5 parameters, 0 given
UseDemoAllLoop (EURUSD,H1) Handle=12
UseDemoAllLoop (EURUSD,H1) Initializing iBands_period_shift_deviation_price() EURUSD, PERIOD_H1
UseDemoAllLoop (EURUSD,H1) iBands_period_shift_deviation_price requires 4 parameters, 0 given
UseDemoAllLoop (EURUSD,H1) Handle=13
UseDemoAllLoop (EURUSD,H1) Initializing iDEMA_period_shift_price() EURUSD, PERIOD_H1
UseDemoAllLoop (EURUSD,H1) iDEMA_period_shift_price requires 3 parameters, 0 given
UseDemoAllLoop (EURUSD,H1) Handle=14
UseDemoAllLoop (EURUSD,H1) Initializing iEnvelopes_period_shift_method_price_deviation() EURUSD, PERIOD_H1
UseDemoAllLoop (EURUSD,H1) iEnvelopes_period_shift_method_price_deviation requires 5 parameters, 0 given
UseDemoAllLoop (EURUSD,H1) Handle=15
...
UseDemoAllLoop (EURUSD,H1) Initializing iVIDyA_momentum_smooth_shift_price() EURUSD, PERIOD_H1
UseDemoAllLoop (EURUSD,H1) iVIDyA_momentum_smooth_shift_price requires 4 parameters, 0 given
UseDemoAllLoop (EURUSD,H1) Handle=22
UseDemoAllLoop (EURUSD,H1) Initializing LifeCycle() EURUSD, PERIOD_H1
UseDemoAllLoop (EURUSD,H1) Handle=10
UseDemoAllLoop (EURUSD,H1) Initializing iAlligator_jawP_jawS_teethP_teethS_lipsP_lipsS_method_price() EURUSD, PERIOD_H1
UseDemoAllLoop (EURUSD,H1) iAlligator_jawP_jawS_teethP_teethS_lipsP_lipsS_method_price requires 8 parameters, 0 given
UseDemoAllLoop (EURUSD,H1) Handle=11
UseDemoAllLoop (EURUSD,H1) Initializing iAMA_period_fast_slow_shift_price() EURUSD, PERIOD_H1
UseDemoAllLoop (EURUSD,H1) iAMA_period_fast_slow_shift_price requires 5 parameters, 0 given
UseDemoAllLoop (EURUSD,H1) Handle=12
UseDemoAllLoop (EURUSD,H1) Initializing iBands_period_shift_deviation_price() EURUSD, PERIOD_H1
UseDemoAllLoop (EURUSD,H1) iBands_period_shift_deviation_price requires 4 parameters, 0 given
UseDemoAllLoop (EURUSD,H1) Handle=13
UseDemoAllLoop (EURUSD,H1) Initializing iDEMA_period_shift_price() EURUSD, PERIOD_H1
UseDemoAllLoop (EURUSD,H1) iDEMA_period_shift_price requires 3 parameters, 0 given
UseDemoAllLoop (EURUSD,H1) Handle=14
UseDemoAllLoop (EURUSD,H1) void OnDeinit(const int)
...
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
Khi chỉ số trong vòng lặp qua mảng các loại chỉ báo đạt đến phần tử cuối cùng và quay lại từ đầu, terminal sẽ bắt đầu trả về các handle của các chỉ báo đã tồn tại cho mã của chúng ta (cùng giá trị: handle 22 được theo sau bởi 10 một lần nữa).