Chờ dữ liệu và quản lý hiển thị (DRAW_NONE
)
Trong chương trước, trong phần Làm việc với mảng tick thực tế trong cấu trúc MqlTick
, chúng ta đã làm việc với script SeriesTicksDeltaVolume.mq5
, script này tính toán khối lượng delta trên mỗi thanh. Lúc đó, chúng ta hiển thị kết quả trong nhật ký, nhưng một cách thuận tiện và logic hơn để phân tích thông tin kỹ thuật như vậy là sử dụng chỉ báo. Trong phần này, chúng ta sẽ tạo một chỉ báo như vậy — IndDeltaVolume.mq5
.
Ở đây, chúng ta sẽ phải đối mặt với hai yếu tố thường gặp khi phát triển chỉ báo nhưng chưa được thảo luận trong các ví dụ trước.
Yếu tố đầu tiên là dữ liệu tick không liên quan đến chuỗi thời gian giá chuẩn, mà terminal gửi đến chỉ báo trong các tham số của OnCalculate
. Điều này có nghĩa là chính chỉ báo phải yêu cầu dữ liệu tick và chờ đợi trước khi có thể hiển thị điều gì đó trong cửa sổ.
Yếu tố thứ hai liên quan đến việc khối lượng mua và bán thường lớn hơn nhiều so với delta của chúng, và khi hiển thị trong cùng một cửa sổ, sẽ khó phân biệt được delta. Tuy nhiên, delta lại là giá trị mang tính chỉ dẫn, thường được phân tích cùng với chuyển động giá. Ví dụ, có 4 tổ hợp rõ ràng nhất của cấu hình thanh và khối lượng delta:
- Thanh tăng giá và delta dương = xác nhận xu hướng tăng.
- Thanh giảm giá và delta âm = xác nhận xu hướng giảm.
- Thanh tăng giá và delta âm = có thể đảo chiều giảm.
- Thanh giảm giá và delta dương = có thể đảo chiều tăng.
Để xem biểu đồ cột của delta, chúng ta cần cung cấp một chế độ để tắt các biểu đồ cột "lớn" (mua và bán), và chúng ta sẽ sử dụng loại DRAW_NONE
cho việc này. Nó vô hiệu hóa việc vẽ một biểu đồ cụ thể và ngăn ảnh hưởng của nó lên tỷ lệ tự động chọn của cửa sổ (nhưng vẫn giữ bộ đệm trong Data Window
). Do đó, bằng cách loại bỏ các biểu đồ lớn khỏi sự xem xét, chúng ta sẽ đạt được tỷ lệ tự động lớn hơn cho biểu đồ delta còn lại. Một cách khác để ẩn bộ đệm bằng cách đánh dấu chúng là phụ trợ (chế độ INDICATOR_CALCULATIONS) sẽ được thảo luận trong phần tiếp theo.
Ý tưởng của delta khối lượng là tính toán riêng khối lượng mua và bán trong các tick, sau đó chúng ta có thể tìm ra sự khác biệt giữa các khối lượng này. Theo đó, chúng ta nhận được ba chuỗi thời gian với khối lượng mua, khối lượng bán và sự khác biệt giữa chúng. Vì thông tin này không phù hợp với thang giá, chỉ báo nên được hiển thị trong cửa sổ riêng của nó, và chúng ta sẽ chọn biểu đồ cột từ zero (DRAW_HISTOGRAM
) làm cách hiển thị ba chuỗi thời gian.
Theo đó, hãy mô tả các thuộc tính của chỉ báo trong các chỉ thị: vị trí, số lượng bộ đệm và biểu đồ, cũng như loại của chúng.
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots 3
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_color1 clrBlue
#property indicator_width1 1
#property indicator_label1 "Buy"
#property indicator_type2 DRAW_HISTOGRAM
#property indicator_color2 clrRed
#property indicator_width2 1
#property indicator_label2 "Sell"
#property indicator_type3 DRAW_HISTOGRAM
#property indicator_color3 clrMagenta
#property indicator_width3 3
#property indicator_label3 "Delta"
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Hãy sử dụng các biến đầu vào từ script trước. Vì tick đại diện cho dữ liệu khá lớn, chúng ta sẽ giới hạn số lượng thanh để tính toán trên lịch sử (BarCount
). Ngoài ra, tùy thuộc vào sự hiện diện hoặc vắng mặt của khối lượng thực trong tick của một công cụ tài chính cụ thể, chúng ta có thể tính toán delta theo hai cách khác nhau, và chúng ta sẽ sử dụng tham số tick type
cho việc này (liệt kê COPY_TICKS
được định nghĩa trong tệp tiêu đề TickEnum.mqh
, mà chúng ta đã sử dụng trong script).
#include <MQL5Book/TickEnum.mqh>
input int BarCount = 100;
input COPY_TICKS TickType = INFO_TICKS;
input bool ShowBuySell = true;
2
3
4
5
Trong trình xử lý OnInit
, chúng ta chuyển đổi chế độ hoạt động của hai biểu đồ cột đầu tiên giữa DRAW_HISTOGRAM
và DRAW_NONE
, tùy thuộc vào tham số ShowBuySell
mà người dùng chọn (mặc định là true
có nghĩa là hiển thị cả ba biểu đồ cột). Lưu ý rằng cấu hình động thông qua PlotIndexSetInteger
ghi đè các cài đặt tĩnh (trong trường hợp này, chỉ một số trong số chúng) được nhúng trong tệp thực thi bằng các chỉ thị #property
.
int OnInit()
{
PlotIndexSetInteger(0, PLOT_DRAW_TYPE, ShowBuySell ? DRAW_HISTOGRAM : DRAW_NONE);
PlotIndexSetInteger(1, PLOT_DRAW_TYPE, ShowBuySell ? DRAW_HISTOGRAM : DRAW_NONE);
return INIT_SUCCEEDED;
}
2
3
4
5
6
7
Nhưng việc đăng ký bộ đệm chỉ báo ở đâu? Chúng ta sẽ quay lại điều đó trong vài đoạn tiếp theo. Bây giờ hãy bắt đầu chuẩn bị hàm OnCalculate
.
int OnCalculate(ON_CALCULATE_STD_FULL_PARAM_LIST)
{
if(prev_calculated == 0)
{
// TODO(1): khởi tạo, điền bằng số không
}
// trên mỗi thanh mới hoặc tập hợp thanh mới trong lần chạy đầu tiên
if(prev_calculated != rates_total)
{
// xử lý tất cả hoặc các thanh mới
for(int i = fmax(prev_calculated, fmax(1, rates_total - BarCount));
i < rates_total && !IsStopped(); ++i)
{
// TODO(2): cố gắng lấy dữ liệu và tính toán thanh thứ i,
// nếu không hoạt động, làm gì đó!
}
}
else // tick trên thanh hiện tại
{
// TODO(3): cập nhật thanh hiện tại
}
return rates_total;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Vấn đề kỹ thuật chính nằm ở khối được gắn nhãn TODO(2)
. Thuật toán yêu cầu tick, được sử dụng trong script và sẽ được chuyển sang chỉ báo với ít thay đổi, yêu cầu chúng bằng hàm CopyTicksRange
. Cuộc gọi như vậy trả về dữ liệu có sẵn trong cơ sở dữ liệu tick. Nhưng nếu dữ liệu chưa có sẵn cho thanh lịch sử đã cho, yêu cầu sẽ khiến dữ liệu tick được tải xuống và đồng bộ hóa không đồng bộ (ở chế độ nền). Trong trường hợp này, mã gọi nhận được 0 tick. Do đó, khi nhận được phản hồi "trống" như vậy, chỉ báo nên ngắt tính toán với dấu hiệu thất bại (nhưng không phải lỗi) và yêu cầu lại tick sau một khoảng thời gian. Trong tình huống thị trường mở bình thường, chúng ta thường xuyên nhận được tick, vì vậy hàm OnCalculate
có lẽ sẽ sớm được gọi lại và tính toán lại với cơ sở tick đã cập nhật. Nhưng cuối tuần khi không có tick thì sao?
Để xử lý chính xác tình huống như vậy, MQL5 cung cấp một bộ đếm thời gian. Chúng ta sẽ nghiên cứu nó trong một trong những chương sau, nhưng bây giờ, chúng ta sẽ sử dụng nó như một "hộp đen". Hàm đặc biệt EventSetTimer
"yêu cầu" nhân gọi chương trình MQL của chúng ta sau một số giây được chỉ định. Điểm vào cho cuộc gọi như vậy là trình xử lý dự trữ OnTimer
, mà chúng ta đã thấy trong bảng tổng quan trong phần Tổng quan về các hàm xử lý sự kiện. Do đó, nếu có sự chậm trễ trong việc nhận dữ liệu tick, bạn nên khởi động bộ đếm thời gian bằng EventSetTimer
(thời gian tối thiểu 1 giây là đủ) và trả về 0 từ OnCalculate
.
int OnCalculate(ON_CALCULATE_STD_FULL_PARAM_LIST)
{
...
for(int i = fmax(prev_calculated, fmax(1, rates_total - BarCount));
i < rates_total && !IsStopped(); ++i)
{
// TODO(2): cố gắng lấy dữ liệu và tính toán thanh thứ i,
if(/*nếu không có dữ liệu*/)
{
Print("Không có dữ liệu trên thanh ", i, ", tại ", TimeToString(time[i]),
". Đặt bộ đếm thời gian để làm mới...");
EventSetTimer(1); // vui lòng gọi lại sau 1 giây
return 0; // chưa hiển thị gì trong cửa sổ
}
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Trong trình xử lý OnTimer
, chúng ta sử dụng hàm EventKillTimer
để dừng bộ đếm thời gian (nếu không làm điều này, hệ thống sẽ tiếp tục gọi trình xử lý của chúng ta mỗi giây). Ngoài ra, chúng ta cần каким-то cách bắt đầu tính toán lại chỉ báo. Cho mục đích này, chúng ta sẽ áp dụng một hàm khác mà chúng ta chưa học trong chương về biểu đồ — ChartSetSymbolPeriod
(xem phần Chuyển đổi biểu tượng và khung thời gian). Nó cho phép đặt một tổ hợp mới của biểu tượng và khung thời gian cho biểu đồ với định danh đã cho (0 nghĩa là biểu đồ hiện tại). Tuy nhiên, nếu chúng không thay đổi bằng cách truyền _Symbol
và _Period
(xem Biến định sẵn), thì biểu đồ sẽ chỉ được cập nhật (các chỉ báo được tính toán lại).
void OnTimer()
{
EventKillTimer();
ChartSetSymbolPeriod(0, _Symbol, _Period); // tự động cập nhật biểu đồ
}
2
3
4
5
Một điểm cần lưu ý ở đây là trong thị trường mở, sự kiện bộ đếm thời gian và tự động cập nhật biểu đồ có thể dư thừa nếu tick tiếp theo xuất hiện trước khi gọi OnTimer
. Do đó, chúng ta sẽ tạo một biến toàn cục (calcDone
) để chuyển đổi cờ trạng thái sẵn sàng của tính toán. Ở đầu OnCalculate
, chúng ta sẽ đặt lại nó thành false
; khi hoàn thành tính toán bình thường, chúng ta sẽ đặt nó thành true
.
bool calcDone = false;
int OnCalculate(ON_CALCULATE_STD_FULL_PARAM_LIST)
{
calcDone = false;
...
if(/*nếu không có dữ liệu*/)
{
...
return 0; // thoát với calcDone = false
}
...
calcDone = true;
return rates_total;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Sau đó, trong OnTimer
, chúng ta có thể khởi tạo tự động cập nhật biểu đồ chỉ khi calcDone
bằng false
.
void OnTimer()
{
EventKillTimer();
if(!calcDone)
{
ChartSetSymbolPeriod(0, _Symbol, _Period);
}
}
2
3
4
5
6
7
8
Bây giờ hãy chuyển sang các ghi chú TODO(1,2,3)
, nơi chúng ta nên thực hiện tính toán và điền vào các bộ đệm chỉ báo. Hãy kết hợp tất cả các thao tác này trong một lớp CalcDeltaVolume
. Do đó, một phương thức riêng sẽ được phân bổ cho mỗi hành động, trong khi chúng ta sẽ giữ trình xử lý OnCalculate
đơn giản (các lệnh gọi phương thức sẽ xuất hiện thay cho ghi chú).
Trong lớp, chúng ta sẽ cung cấp các biến thành viên chấp nhận cài đặt người dùng cho số lượng thanh lịch sử được xử lý và phương pháp tính toán delta, cũng như ba mảng cho bộ đệm chỉ báo. Hãy khởi tạo chúng trong hàm tạo.
class CalcDeltaVolume
{
const int limit;
const COPY_TICKS tickType;
double buy[];
double sell[];
double delta[];
public:
CalcDeltaVolume(
const int bars,
const COPY_TICKS type)
: limit(bars), tickType(type), lasttime(0), lastcount(0)
{
// đăng ký các mảng nội bộ làm bộ đệm chỉ báo
SetIndexBuffer(0, buy);
SetIndexBuffer(1, sell);
SetIndexBuffer(2, delta);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Chúng ta có thể gán các mảng thành viên làm bộ đệm vì chúng ta sẽ tạo một đối tượng toàn cục của lớp này tiếp theo. Để hiển thị dữ liệu chính xác, chúng ta chỉ cần đảm bảo rằng các mảng gắn vào biểu đồ tồn tại tại thời điểm vẽ. Có thể thay đổi liên kết bộ đệm động (xem ví dụ của IndSubChartSimple.mq5
trong phần tiếp theo).
Lưu ý rằng bộ đệm chỉ báo phải có kiểu double
trong khi khối lượng có kiểu ulong
. Do đó, đối với các giá trị rất lớn (ví dụ, trên khung thời gian rất lớn), có thể xảy ra mất độ chính xác theo giả thuyết.
Phương thức reset
được tạo để khởi tạo bộ đệm. Hầu hết các phần tử mảng được điền bằng giá trị trống EMPTY_VALUE
, và limit
thanh cuối cùng được điền bằng số không vì ở đó chúng ta sẽ tổng hợp khối lượng mua và bán riêng biệt.
void reset()
{
// điền vào mảng mua và sao chép phần còn lại từ nó
// giá trị trống trong tất cả các phần tử trừ limit thanh cuối cùng bằng 0
ArrayInitialize(buy, EMPTY_VALUE);
ArrayFill(buy, ArraySize(buy) - limit, limit, 0);
// sao chép trạng thái ban đầu vào các mảng khác
ArrayCopy(sell, buy);
ArrayCopy(delta, buy);
}
2
3
4
5
6
7
8
9
10
11
Tính toán trên thanh lịch sử thứ i được thực hiện bởi phương thức createDeltaBar
. Đầu vào của nó nhận số thanh và một liên kết đến mảng với dấu thời gian của các thanh (chúng ta nhận nó dưới dạng tham số OnCalculate
). Các phần tử mảng thứ i được khởi tạo bằng số không.
int createDeltaBar(const int i, const datetime &time[])
{
delta[i] = buy[i] = sell[i] = 0;
...
2
3
4
Sau đó, chúng ta cần đến giới hạn thời gian của thanh thứ i: prev
và next
, trong đó next
được tính sang phải của prev
bằng cách cộng giá trị của hàm PeriodSeconds
mới đối với chúng ta. Nó trả về số giây trong khung thời gian hiện tại. Bằng cách cộng số lượng này, chúng ta tìm ra điểm bắt đầu lý thuyết của thanh tiếp theo. Trong lịch sử, khi i
không bằng số của thanh cuối cùng, chúng ta có thể thay việc tìm dấu thời gian tiếp theo bằng time[i + 1]
. Tuy nhiên, chỉ báo cũng nên hoạt động trên thanh cuối cùng vẫn đang hình thành và không có thanh tiếp theo. Do đó, nói chung, việc sử dụng time[i + 1]
là không được phép.
...
const datetime prev = time[i];
const datetime next = prev + PeriodSeconds();
2
3
Khi chúng ta thực hiện tính toán tương tự trong script, chúng ta không phải sử dụng hàm PeriodSeconds
, vì chúng ta không tính thanh cuối cùng, hiện tại và có thể tìm next
và prev
, như iTime(WorkSymbol, TimeFrame, i)
và iTime(WorkSymbol, TimeFrame, i + 1)
, tương ứng.
Tiếp theo, trong phương thức createDeltaBar
, chúng ta yêu cầu tick trong các dấu thời gian đã tìm thấy (trừ 1 mili giây từ bên phải để không chạm vào thanh tiếp theo). Tick đến trong mảng ticks
, được xử lý bởi phương thức trợ giúp calc
. Nó chứa thuật toán script gần như không thay đổi. Chúng ta buộc phải tách nó thành một phương thức riêng vì tính toán sẽ được thực hiện trong hai tình huống khác nhau: sử dụng các thanh lịch sử (nhớ ghi chú TODO(2)
) và sử dụng tick trên thanh hiện tại (ghi chú TODO(3)
). Hãy xem xét tình huống thứ hai dưới đây.
ResetLastError();
MqlTick ticks[];
const int n = CopyTicksRange(_Symbol, ticks, COPY_TICKS_ALL,
prev * 1000, next * 1000 - 1);
if(n > -1 && _LastError == 0)
{
calc(i, ticks);
}
else
{
return -_LastError;
}
return n;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
Trong trường hợp yêu cầu thành công, phương thức trả về số lượng tick đã xử lý, và trong trường hợp lỗi, nó trả về mã lỗi với dấu trừ. Lưu ý rằng nếu chưa có tick cho thanh trong cơ sở dữ liệu (điều này không phải là lỗi, nghiêm túc mà nói, nhưng nó không cho phép hoạt động trực quan của chỉ báo tiếp tục), phương thức sẽ trả về 0 (dấu của 0 không thay đổi giá trị của nó). Do đó, trong hàm OnCalculate
, chúng ta cần kiểm tra kết quả của phương thức với "nhỏ hơn hoặc bằng" 0.
Phương thức calc
thực tế bao gồm các dòng làm việc của script SeriesTicksDeltaVolume.mq5
, vì vậy chúng ta sẽ không trình bày nó ở đây. Những ai muốn làm mới trí nhớ có thể xem trong IndDeltaVolume.mq5
.
Để tính toán delta trên thanh cuối cùng được cập nhật liên tục, chúng ta cần cố định dấu thời gian của tick cuối cùng đã xử lý với độ chính xác mili giây. Sau đó, trong lần gọi tiếp theo của OnCalculate
, chúng ta sẽ có thể truy vấn tất cả các tick sau nhãn này.
Lưu ý rằng không có đảm bảo rằng hệ thống sẽ kịp gọi trình xử lý OnCalculate
của chúng ta trên mỗi tick trong thời gian thực. Nếu chúng ta thực hiện các tính toán nặng, hoặc nếu một chương trình MQL khác làm tải terminal với các tính toán, hoặc nếu tick đến rất nhanh (ví dụ, khi sau các bản tin quan trọng), các sự kiện có thể không vào được hàng đợi chỉ báo (không quá một sự kiện của mỗi loại được lưu trữ trong hàng đợi, bao gồm không quá một thông báo tick). Do đó, nếu chương trình muốn nhận tất cả các tick, nó phải yêu cầu chúng bằng CopyTicksRange
hoặc CopyTicks
.
Tuy nhiên, chỉ dấu thời gian của tick cuối cùng đã xử lý là không đủ. Các tick có thể có cùng thời gian ngay cả khi tính đến mili giây. Do đó, chúng ta không thể cộng 1 mili giây vào nhãn để loại trừ tick "cũ": các tick "mới" có cùng nhãn có thể xuất hiện sau nó.
Về vấn đề này, bạn nên nhớ không chỉ nhãn mà còn số lượng tick cuối cùng với nhãn này. Sau đó, lần tiếp theo chúng ta yêu cầu tick, chúng ta có thể thực hiện điều đó bắt đầu từ thời gian đã nhớ (tức là bao gồm các tick "cũ"), nhưng bỏ qua chính xác số lượng đã xử lý lần trước.
Để thực hiện thuật toán này, hai biến được khai báo trong lớp: last time
và last count
.
ulong lasttime; // nhãn mili giây của tick trực tuyến cuối cùng đã xử lý
int lastcount; // số lượng tick với nhãn này tại thời điểm đó
2
Từ mảng tick nhận được từ hệ thống, chúng ta tìm các giá trị cho các biến này bằng phương thức trợ giúp updateLastTime
.
void updateLastTime(const int n, const MqlTick &ticks[])
{
lasttime = ticks[n - 1].time_msc;
lastcount = 0;
for(int k = n - 1; k >= 0; --k)
{
if(ticks[k].time_msc == ticks[n - 1].time_msc) ++lastcount;
}
}
2
3
4
5
6
7
8
9
Bây giờ chúng ta có thể tinh chỉnh phương thức createDeltaBar
: khi xử lý thanh cuối cùng, chúng ta gọi updateLastTime
lần đầu tiên.
int createDeltaBar(const int i, const datetime &time[])
{
...
const int size = ArraySize(time);
const int n = CopyTicksRange(_Symbol, ticks, COPY_TICKS_ALL,
prev * 1000, next * 1000 - 1);
if(n > -1 && _LastError == 0)
{
if(i == size - 1) // thanh cuối cùng
{
updateLastTime(n, ticks);
}
calc(i, ticks);
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Với các giá trị cập nhật cho last time
và last count
, chúng ta có thể triển khai một phương thức để tính toán delta trên thanh hiện tại trực tuyến.
int updateLastDelta(const int total)
{
MqlTick ticks[];
ResetLastError();
const int n = CopyTicksRange(_Symbol, ticks, COPY_TICKS_ALL, lasttime);
if(n > -1 && _LastError == 0)
{
const int skip = lastcount;
updateLastTime(n, ticks);
calc(total - 1, ticks, skip);
return n - skip;
}
return -_LastError;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
Để triển khai chế độ này, chúng ta đã giới thiệu một tham số tùy chọn bổ sung skip
trong phương thức calc
. Nó cho phép bỏ qua tính toán trên một số lượng tick "cũ" đã cho.
void calc(const int i, const MqlTick &ticks[], const int skip = 0)
{
const int n = ArraySize(ticks);
for(int j = skip; j < n; ++j)
...
}
2
3
4
5
6
Lớp tính toán đã sẵn sàng. Bây giờ, chúng ta chỉ cần chèn các lệnh gọi đến ba phương thức công khai vào OnCalculate
.
int OnCalculate(ON_CALCULATE_STD_FULL_PARAM_LIST)
{
if(prev_calculated == 0)
{
deltas.reset(); // khởi tạo, điền bằng số không
}
calcDone = false;
// trên mỗi thanh mới hoặc tập hợp thanh mới trong lần chạy đầu tiên
if(prev_calculated != rates_total)
{
// xử lý tất cả hoặc các thanh mới
for(int i = fmax(prev_calculated, fmax(1, rates_total - BarCount));
i < rates_total && !IsStopped(); ++i)
{
// cố gắng lấy dữ liệu và tính toán thanh thứ i,
if((deltas.createDeltaBar(i, time)) <= 0)
{
Print("Không có dữ liệu trên thanh ", i, ", tại ", TimeToString(time[i]),
". Đặt bộ đếm thời gian để làm mới...");
EventSetTimer(1); // gọi lại sau 1 giây
return 0; // chưa hiển thị gì trong cửa sổ
}
}
}
else // tick trên thanh hiện tại
{
if((deltas.updateLastDelta(rates_total)) <= 0)
{
return 0; // lỗi
}
}
calcDone = true;
return rates_total;
}
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
Hãy biên dịch và chạy chỉ báo. Để bắt đầu, nên chọn khung thời gian không cao hơn H1 và để số lượng thanh trong BarCount
mặc định là 100. Sau khi chờ một chút để chỉ báo xây dựng, kết quả sẽ trông giống như sau:
Chỉ báo khối lượng delta với tất cả các biểu đồ cột, bao gồm mua và bán
Bây giờ hãy so sánh với điều gì sẽ xảy ra khi đặt tham số ShowBuySell
thành false
.
Chỉ báo khối lượng với một biểu đồ cột của delta (mua và bán riêng lẻ bị ẩn)
Vì vậy, trong chỉ báo này, chúng ta đã triển khai việc chờ tải dữ liệu tick cho công cụ làm việc bằng bộ đếm thời gian, vì tick có thể yêu cầu tài nguyên đáng kể. Trong phần tiếp theo, chúng ta sẽ xem xét các chỉ báo đa tiền tệ hoạt động ở cấp độ báo giá, và do đó một yêu cầu không đồng bộ đơn giản để cập nhật biểu đồ bằng ChartSetSymbolPeriod
sẽ đủ cho chúng. Sau này, chúng ta sẽ phải triển khai một loại chờ khác để đảm bảo chuỗi thời gian của một chỉ báo khác đã sẵn sàng.