Professional Documents
Culture Documents
PDF Translator 1695888197567
PDF Translator 1695888197567
Ố Ấ
ngoài=0; // PRICE_CLOSE 99 www.ZTCprep.com CHƯƠNG TRÌNH CỐ VẤN CHUYÊN
GIA Chúng tôi sẽ sử dụng các mã định danh SlopePeriod, SlopeMethod và SlopePrice
cho các biến bên ngoài trong Expert Advisor của chúng tôi. // Biến ngoài extern int
SlopePeriod = 80; extern int SlopeMethod = 3; extern int SlopePrice = 0; Đây là cách
hàm iCustom() sẽ tìm kiếm chỉ báo cụ thể này, cùng với các biến bên ngoài:
iCustom(NULL,0,"Slope Direction Line",SlopePeriod,SlopeMethod,SlopePrice,0,0);
NULL chỉ ra rằng chúng tôi đang sử dụng ký hiệu biểu đồ hiện tại và 0 là khoảng thời
gian biểu đồ hiện tại. "Đường hướng dốc" là tên của tệp chỉ báo. SlopePeriod,
SlopeMethod và SlopePrice là ba thông số chỉ báo. Chúng tôi đang sử dụng chỉ mục
Chế độ mặc định là 0 và Shift là thanh hiện tại. Mặc dù chỉ báo Đường hướng dốc
được vẽ dưới dạng một đường duy nhất nhưng thực tế nó bao gồm hai vùng đệm khác
nhau. Tùy thuộc vào việc giá chỉ báo tăng hay giảm, màu sắc (và vùng đệm) sẽ thay
đổi. Nếu bạn đính kèm chỉ báo vào biểu đồ và xem Cửa sổ dữ liệu trong MetaTrader,
bạn sẽ thấy hai giá trị cho Đường hướng dốc. Giá trị đầu tiên hiển thị giá khi giá trị chỉ
báo tăng. Dòng này có màu xanh theo mặc định. Giá trị thứ hai hiển thị giá khi giá trị
chỉ báo giảm. Dòng này mặc định có màu đỏ. Chúng ta cần xác định chỉ số Mode cho
cả 2 dòng này. Cách dễ nhất để làm điều này là xem mã nguồn. Trong hàm init(), bạn
sẽ thấy một số dòng mã được sử dụng để khai báo và thiết lập các thuộc tính cho bộ
đệm chỉ báo: Hình 6.2 – Data Window SetIndexBuffer(0, Uptrend); SetIndexBuffer(1,
Dntrend); SetIndexBuffer(2, ExtMapBuffer); ...
SetIndexStyle(0,DRAW_LINE,STYLE_SOLID,2);
SetIndexStyle(1,DRAW_LINE,STYLE_SOLID,2); 100 www.ZTCprep.com Điều kiện và chỉ
báo đơn hàng Hàm SetIndexBuffer() đầu tiên thiết lập bộ đệm chỉ báo có chỉ số là 0 và
sử dụng mảng Uptrend. Chúng ta có thể đoán từ tên mảng mà điều này áp dụng cho
đường chỉ báo màu xanh lam. Hàm thứ hai cũng thực hiện tương tự đối với mảng
DnTrend. Lưu ý các hàm SetIndexStyle() ở phía dưới đặt bộ đệm 0 và 1 để vẽ một
đường liền nét. Bộ đệm thứ ba, với chỉ số 2 và mảng ExtMapBuffer, chỉ được sử dụng
để tính toán. Do đó, chúng tôi có thể kết luận rằng 0 và 1 là các chỉ số đệm chứa thông
tin về giá chỉ báo của chúng tôi. Dựa trên các mã định danh mảng, 0 là đường xu
hướng tăng và 1 là xu hướng giảm. Đây là cách chúng tôi khai báo các chỉ báo của
mình: double SlopeUp = iCustom(NULL,0,"Slope Direction
Line",SlopePeriod,SlopeMethod, SlopePrice,0,1); double SlopeDown =
iCustom(NULL,0,"Slope Direction Line",SlopePeriod,SlopeMethod, SlopePrice,1,1); Lưu
ý rằng tham số Chế độ – tham số tiếp theo cuối cùng – đã được đặt thành chỉ số đệm
chỉ báo thích hợp – 0 cho SlopeUp và 1 cho SlopeDown. Tham số Shift – tham số cuối
cùng – đã được đặt thành 1, kiểm tra giá trị đóng của thanh cuối cùng. Bạn nên kiểm
tra kỹ xem bạn có đang sử dụng đúng thông số Chế độ hay không. Thêm chức năng
Print() vào cố vấn chuyên gia của bạn và chạy thử nghiệm ngược trong Trình kiểm tra
chiến lược bằng cách sử dụng "Chỉ giá mở" làm mô hình thử nghiệm. Đảm bảo tham
số Shift được đặt thành 1 trong hàm iCustom(). Print("Dốc lên: "+SlopeUp+", Dốc
xuống: "+SlopeDown+" Thời gian: "+TimeToStr(Time[1])); Hàm Print() in giá trị của bộ
đệm chỉ báo của chúng tôi vào nhật ký, cùng với ngày và giờ của thanh trước đó. Bạn
có thể xem nhật ký trong tab Nhật ký trong cửa sổ Trình kiểm tra chiến lược. Đây là
đầu ra của hàm Print() trong nhật ký: Slope Up: 2147483647.00000000, Slope Down:
1.50483900 Thời gian: 2009.11.26 16:00 Giá trị cho SlopeUp, 2147483647, là một số
nguyên rất lớn biểu thị trạng thái EMPTY_VALUE của một chỉ báo tùy chỉnh. Bạn thực
sự có thể sử dụng điều này như một điều kiện giao dịch. SlopeDown trả về giá trị chỉ
báo của thanh trước đó. Thời gian chỉ ra thanh mà chúng ta muốn tìm trên biểu đồ.
Nhấp vào nút Mở biểu đồ trong cửa sổ Trình kiểm tra chiến lược để mở biểu đồ với chỉ
báo của bạn đã được áp dụng. Tìm thanh được chỉ định trong nhật ký theo Thời gian
và đảm bảo các giá trị chỉ báo trong Cửa sổ dữ liệu khớp với giá trị được in trong nhật
ký. Nếu không, hãy điều chỉnh tham số Chế độ trong hàm iCustom() cho đến khi bạn
tìm thấy bộ đệm chính xác. 101 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA
Đây là cách chúng tôi sử dụng chỉ báo Đường định hướng dốc trong cố vấn chuyên gia
của mình. Nếu độ dốc có xu hướng đi lên, SlopeUp sẽ trả về giá trị giá, trong khi
SlopeDown sẽ trả về EMPTY_VALUE hoặc 2147483647. Điều ngược lại áp dụng khi độ
dốc có xu hướng đi xuống. if(SlopeUp != EMPTY_VALUE && SlopeDown ==
EMPTY_VALUE) // Mua if(SlopeUp == EMPTY_VALUE && SlopeDown !=
EMPTY_VALUE) // Bán Những điều kiện này chỉ cần kiểm tra xem dòng nào bằng
EMPTY_VALUE và dòng nào không. Hằng số chỉ báo Khung thời gian Nhiều hàm trong
MQL, bao gồm các hàm chỉ báo và giá, chấp nhận tham số khung thời gian. Như đã
chỉ ra trước đó, nếu chúng ta sử dụng tham số Khung thời gian bằng 0 thì khung thời
gian biểu đồ hiện tại sẽ được sử dụng. Nếu chúng tôi muốn sử dụng khung thời gian
khác, chúng tôi sẽ cần chỉ định khung thời gian tính bằng phút. Ví dụ: M5 là 5, H1 là 60
và H4 là 240. Chúng ta cũng có thể sử dụng hằng số để biểu thị khung thời gian: •
PERIOD_M1 – 1 phút. • PERIOD_M5 – 5 phút. • PERIOD_M15 – 15 phút. • PERIOD_M30
– 30 phút. • PERIOD_H1 – 1 giờ (60 phút). • PERIOD_H4 – 4 giờ (240 phút). •
PERIOD_D1 – Hàng ngày (1440 phút). Giá áp dụng Tham số chỉ báo giá được áp dụng
cho biết chuỗi giá sẽ sử dụng khi tính giá trị chỉ báo. Nói chung, bạn sẽ sử dụng giá trị
đóng để tính toán các giá trị chỉ báo, mặc dù bạn cũng có thể muốn sử dụng các giá
trị khác. Đây là danh sách chuỗi giá và các hằng số liên quan của chúng, cùng với giá
trị nguyên: • PRICE_CLOSE – 0: Giá đóng. • PRICE_OPEN – 1: Giá mở cửa. •
PRICE_HIGH – 2: Giá cao. 102 www.ZTCprep.com Điều kiện đặt hàng và chỉ số •
PRICE_LOW – 3: Giá thấp. • PRICE_MEDIAN – 4: Giá trung bình, (Cao+Thấp)/2. •
PRICE_TYPICAL – 5: Giá thông thường, (Cao+Thấp+Đóng)/3. • PRICE_WEIGHTED – 6:
Giá bình quân, (Cao+Thấp+Đóng+Đóng)/4. Phương pháp trung bình trượt Các chỉ báo
sử dụng đường trung bình động như một phần trong phép tính của chúng có thể có
tham số để điều chỉnh phương pháp tính đường trung bình động. Đường trung bình
động sẽ được vẽ khác nhau tùy theo phương pháp tính toán. Dưới đây là các hằng số
phương pháp trung bình động với các giá trị nguyên tương ứng: • MODE_SMA – 0:
Trung bình động đơn giản. Tính giá trị trung bình của dữ liệu giá. • MODE_EMA – 1:
Đường trung bình động hàm mũ. Cung cấp nhiều trọng số hơn cho dữ liệu giá gần đây
và giảm trọng số theo cấp số nhân đối với dữ liệu giá cũ hơn. Một đường trung bình
động rất phổ biến. • MODE_SMMA – 2: Đường trung bình động được làm mịn. Một
đường trung bình động đơn giản được tính bằng phương trình làm mịn. Tạo ra một
đường nét mượt mà nhưng kém phản hồi. • MODE_LWMA – 3: Đường trung bình động
có trọng số tuyến tính. Tương tự như đường trung bình động hàm mũ, nhưng có ảnh
hưởng lớn hơn đến mức giá hiện tại nhất. Đánh giá các điều kiện giao dịch Chúng tôi
sử dụng các toán tử có điều kiện if và else để đánh giá các điều kiện giao dịch của
mình. Bạn đã thấy những điều này được sử dụng trong cuốn sách này, nhưng đối với
những người lập trình mới, bạn nên xem lại nhanh. Toán tử if đánh giá một điều kiện
đúng hoặc sai. Nếu điều kiện đúng, mã ngay sau câu lệnh if sẽ được thực thi. Nếu điều
kiện sai, nó sẽ chuyển sang đoạn mã tiếp theo khối if: if(BuyCondition == true) {
OpenBuyOrder(...); } Nếu chỉ có một câu lệnh theo sau toán tử if, nó có thể được viết
như sau: if(BuyCondition == true) OpenBuyOrder(...); 103 www.ZTCprep.com LẬP
TRÌNH TƯ VẤN CHUYÊN GIA Nhiều câu lệnh phải được đặt trong dấu ngoặc nhọn.
Toán tử else đánh giá một điều kiện thay thế, miễn là (các) câu lệnh if trước đó là sai.
Bạn có thể kết hợp else và if để tạo một điều kiện thay thế sẽ chỉ được thực thi nếu nó
đúng. Ví dụ: mã này đánh giá ba điều kiện theo thứ tự. Nếu một trong số đó đúng thì
chỉ khối mã đó sẽ được thực thi. Nếu không có điều kiện nào đúng thì sẽ không có
điều kiện nào được thực thi: if(Condition1 == true) // Thực thi điều kiện 1 else
if(Condition2 == true) // Thực thi điều kiện 2 else if(Condition3 == true) // Thực thi điều
kiện 3 Toán tử else có thể được sử dụng một mình ở cuối chuỗi if-else để biểu thị điều
kiện sẽ được thực thi theo mặc định nếu tất cả các toán tử if còn lại đều sai. Như trên,
chỉ một trong các điều kiện sẽ được thực thi: if(Condition1 == true) // Thực thi điều
kiện 1 else if(Condition2 == true) // Thực thi điều kiện 2 else { // Thực thi điều kiện 3
nếu 1 và 2 sai } Nếu bạn có nhiều toán tử if mà không có bất kỳ toán tử else nào, thì
mỗi toán tử sẽ được thực thi nếu nó đúng – việc câu lệnh if tiếp theo đúng hay sai
không quan trọng: if(Condition1 == true) // Thực thi điều kiện 1 if (Condition2 == true)
// Thực thi điều kiện 2 Các phép toán quan hệ Chúng ta bắt đầu đánh giá các điều kiện
đúng và sai bằng cách so sánh các giá trị sử dụng lớn hơn, nhỏ hơn, bằng, không
bằng, v.v. Đây là danh sách các phép toán quan hệ: • == Bằng To – Nếu x == y, điều
kiện là đúng. • > Lớn Hơn – Nếu x > y, điều kiện là đúng. • < Nhỏ hơn – Nếu x < y, điều
kiện đúng. • >= Lớn hơn hoặc Bằng – Nếu x >= y, điều kiện là đúng. 104
www.ZTCprep.com Điều kiện và chỉ báo đơn hàng • <= Nhỏ hơn hoặc bằng – nếu x <=
y, điều kiện là đúng. • != Không bằng – Nếu x != y, điều kiện là đúng. Lưu ý rằng toán tử
bằng (==) không giống với toán tử gán (=)! Toán tử gán được sử dụng khi gán giá trị
cho một biến. Toán tử bằng được sử dụng để đánh giá điều kiện đúng/sai. Đây là một
lỗi cú pháp phổ biến và bạn nên chú ý. Bạn có thể so sánh hai giá trị bất kỳ miễn là
chúng có cùng kiểu dữ liệu. Bạn có thể so sánh giá trị boolean với các hằng số đúng
hoặc sai. Bạn có thể so sánh một chuỗi, số nguyên hoặc biến kép với một giá trị không
đổi thích hợp hoặc với một biến khác cùng loại. Các phép toán Boolean Chúng ta sử
dụng các toán tử boolean AND (&&) và OR (||) để kết hợp các phép toán quan hệ. Toán
tử AND đánh giá xem tất cả các điều kiện có đúng hay không. Nếu vậy, toàn bộ tuyên
bố là đúng. Nếu bất kỳ điều kiện nào sai thì toàn bộ câu lệnh là sai. if(BooleanVar1 ==
true && Indicator1 > Indicator2) { // Lệnh mở } Nếu BooleanVar1 bằng true và
Indicator1 lớn hơn Indicator2, câu lệnh sẽ đánh giá là đúng và mã giữa các dấu ngoặc
nhọn sẽ được chạy. Nếu một trong hai điều kiện này sai thì toàn bộ câu lệnh sẽ đánh
giá là sai và mã trong dấu ngoặc nhọn sẽ không được chạy. Có thể có bất kỳ số lượng
điều kiện nào được kết hợp cùng với toán tử && và tất cả chúng đều phải đánh giá là
đúng. Toán tử OR đánh giá liệu một trong các điều kiện có đúng hay không. Nếu ít
nhất một điều kiện đúng thì toàn bộ câu lệnh sẽ được đánh giá là đúng. Nếu tất cả các
điều kiện đều sai, câu lệnh sẽ đánh giá là sai. if(BooleanVar1 == true || Indicator1 >
Indicator2) Nếu BooleanVar1 bằng true hoặc Indicator1 lớn hơn Indicator2 thì câu lệnh
được đánh giá là đúng. Nếu cả hai điều kiện này đều sai thì câu lệnh sẽ đánh giá là
sai. Bạn có thể kết hợp các thao tác AND và OR để tạo các điều kiện giao dịch phức
tạp hơn. Khi làm như vậy, hãy sử dụng dấu ngoặc đơn để thiết lập thứ tự thực hiện các
thao tác. 105 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN NGHIỆP
if((BooleanVar1 == true && Indicator1 > Indicator2) || BooleanVar1 == false) Câu lệnh
(BooleanVar1 == true && Indicator1 > Indicator2) được đánh giá đầu tiên. Nếu cả hai
điều kiện này đều đúng thì câu lệnh sẽ đánh giá là đúng và chúng ta còn lại phép toán
OR: if(true || BooleanVar1 == false) Câu lệnh này tự động đánh giá là đúng, vì một
trong các điều kiện đã đúng. Nhưng điều gì sẽ xảy ra nếu (BooleanVar1 == true &&
Indicator1 > Indicator2) đánh giá là sai? if(false || BooleanVar1 == false) Nếu điều kiện
BooleanVar1 == false có giá trị đúng thì toàn bộ câu lệnh là đúng. (Nói cách khác, nếu
BooleanVar1 được đặt thành false thì điều kiện đó sẽ có giá trị là true.) Ngược lại, câu
lệnh là sai. Có thể tạo các phép toán boolean phức tạp bằng cách sử dụng AND, OR và
dấu ngoặc đơn để kiểm soát thứ tự các phép toán. Hãy nhớ xem vị trí của dấu ngoặc
đơn, vì một dấu ngoặc đơn sai có thể khiến câu lệnh được đánh giá khác và việc thiếu
dấu ngoặc đơn có thể dẫn đến một số lỗi tẻ nhạt. Bật và tắt chỉ báo Bạn có thể sử
dụng ví dụ AND/OR trong phần trước để bật và tắt chỉ báo. Giả sử EA của bạn sử dụng
nhiều chỉ báo và bạn muốn có thể bật và tắt các chỉ báo. Đây là cách chúng tôi làm
điều đó. Đầu tiên, hãy khai báo một biến boolean bên ngoài để sử dụng làm công tắc
bật/tắt. Chúng ta sẽ sử dụng chỉ báo ngẫu nhiên trong ví dụ này: extern bool
UseStochastic = true; Chúng tôi xác định hai bộ điều kiện cho chỉ báo của mình –
trạng thái "bật" và trạng thái "tắt". Trạng thái bật bao gồm biến bật/tắt được đặt thành
true, cùng với điều kiện mở lệnh. Trạng thái tắt chỉ đơn giản bao gồm biến bật/tắt
được đặt thành sai. if((UseStochastic == true && Kline > Dline) || UseStochastic ==
false) { // Lệnh mua } 106 www.ZTCprep.com Điều kiện và chỉ báo lệnh Lệnh
(UseStochastic == true && Kline > Dline) là " của chúng tôi trạng thái bật". Nếu biến
bên ngoài UseStochastic được đặt thành true và điều kiện giao dịch Kline > Dline đánh
giá là true thì điều kiện lệnh ngẫu nhiên sẽ đúng. UseStochastic == false là trạng thái
"tắt" của chúng tôi. Nếu biến bên ngoài UseStochastic được đặt thành false thì
(UseStochastic == true && Kline > Dline) đánh giá là sai, trong khi UseStochastic ==
false đánh giá là đúng. Vì trạng thái bật và tắt được liên kết bởi toán tử OR nên chỉ một
trong số chúng phải đúng để làm cho toàn bộ câu lệnh trở thành đúng. Vì vậy, miễn là
a.) đèn báo bật và điều kiện đặt lệnh hợp lệ; hoặc b.) đèn báo tắt; toàn bộ tuyên bố sẽ
đúng và mọi điều kiện đơn hàng còn lại đều có thể được đánh giá. Hãy thêm điều kiện
giao dịch thứ hai vào điều kiện ngẫu nhiên của chúng ta – đường chéo trung bình
động: if(((UseStochastic == true && Kline > Dline) || UseStochastic == false) &&
FastMA > SlowMA) Trong ví dụ này, chúng tôi đã thêm điều kiện chéo trung bình động,
FastMA > SlowMA. Lưu ý rằng chúng tôi đã thêm một tập hợp dấu ngoặc đơn khác
xung quanh điều kiện ngẫu nhiên, vì toàn bộ câu lệnh trong dấu ngoặc đơn cần được
đánh giá trước tiên. Đầu tiên, chúng tôi đánh giá câu lệnh bên trong tập hợp dấu ngoặc
đơn trong cùng: (UseStochastic == true && Kline > Dline). Nếu tham số UseStochastic
được đặt thành true và Kline > Dline đánh giá là true thì phần đầu tiên của câu lệnh là
đúng. if((true || UseStochastic == false) && FastMA > SlowMA) Điều kiện
UseStochastic == false đánh giá thành sai. Chúng ta chỉ còn lại phép toán OR và vì
một trong các điều kiện đã đúng nên toàn bộ điều kiện ngẫu nhiên đánh giá là đúng:
if((true || false) && FastMA > SlowMA) if(true && FastMA > SlowMA) If FastMA >
SlowMA đánh giá là đúng, toàn bộ điều kiện giao dịch là đúng và lệnh được đặt. Nếu
nó sai, câu lệnh sẽ đánh giá là sai và lệnh không được đặt. Bây giờ, điều gì sẽ xảy ra
nếu điều kiện giao dịch ngẫu nhiên là sai? Nếu UseStochastic được đặt thành true và
Kline > Dline đánh giá là sai, thì toàn bộ điều kiện trở thành sai: 107
www.ZTCprep.com CHƯƠNG TRÌNH TƯ VẤN CHUYÊN NGHIỆP if((false ||
UseStochastic == false) && FastMA > SlowMA) if((false || false) && FastMA >
SlowMA) if(false && FastMA > SlowMA) Bất kể FastMA > SlowMA đánh giá như thế
nào, toàn bộ điều kiện giao dịch đều sai. Bây giờ giả sử UseStochastic được đặt thành
false. Trong trường hợp này, câu lệnh (UseStochastic == true && Kline > Dline) có giá
trị sai: if((false || UseStochastic == false) && FastMA > SlowMA) Vì câu lệnh
UseStochastic == false là đúng nên điều kiện ngẫu nhiên sẽ đánh giá thành sự thật.
if((false || true) && FastMA > SlowMA) if(true && FastMA > SlowMA) Điều đó có nghĩa
là nếu FastMA > SlowMA cũng có giá trị đúng thì đơn hàng sẽ được đặt. Trong trường
hợp này, điều kiện ngẫu nhiên thậm chí không được xem xét, ngoài việc đánh giá trạng
thái bật/tắt của chỉ báo. So sánh các giá trị chỉ báo giữa các thanh Đôi khi bạn sẽ cần
so sánh giá trị chỉ báo của thanh hiện tại hoặc đóng gần đây nhất với giá trị chỉ báo
của thanh trước đó. Ví dụ: giả sử bạn muốn biết liệu đường trung bình động đang tăng
hay giảm. Để làm điều này, chúng tôi so sánh chỉ số của thanh hiện tại với thanh trước
đó. Chúng tôi sử dụng tham số Shift của hàm chỉ báo để xác định thanh nào sẽ trả về
giá trị chỉ báo. Tham số Shift luôn là tham số cuối cùng trong hàm chỉ báo. Thanh hiện
tại có độ dịch chuyển là 0, thanh trước đó có độ dịch chuyển là 1, v.v. Các hàm trung
bình động bên dưới sẽ trả về giá trị trung bình động cho thanh hiện tại và thanh trước
đó: double MA = iMA(NULL,0,MAPeriod,0,MAMethod,MAprice,0); nhân đôi LastMA =
iMA(NULL,0,MAPeriod,0,MAMethod,MAprice,1); 108 www.ZTCprep.com Điều kiện và
chỉ báo lệnh Trong ví dụ này, MA là biến giữ giá trị chỉ báo của thanh hiện tại, trong khi
LastMA giữ giá trị chỉ báo của thanh trước đó. Lưu ý rằng tham số Shift là 0 cho thanh
hiện tại và 1 cho thanh trước đó. Đây là đoạn mã để xác định xem một đường trung
bình động đang di chuyển lên hay xuống: if(MA > LastMA) { // MA đang tăng } else
if(MA < LastMA) { // MA đang đi xuống } Nếu giá trị chỉ báo của thanh hiện tại (MA)
lớn hơn giá trị của thanh trước đó (LastMA), chúng ta có thể kết luận rằng chỉ báo
đang di chuyển lên. Điều ngược lại đúng khi giá trị chỉ báo của thanh hiện tại nhỏ hơn
giá trị chỉ báo của thanh trước đó. Bằng cách so sánh giá trị chỉ báo của thanh trước
đó với thanh hiện tại, chúng ta có thể xác định liệu chỉ báo gần đây đã vượt lên trên
hay xuống dưới một giá trị nhất định, chẳng hạn như mức quá mua/quá bán của một
bộ dao động hoặc một đường chỉ báo khác. Ví dụ: giả sử hệ thống giao dịch của bạn
đưa ra tín hiệu giao dịch khi ngẫu nhiên vượt qua trên 30 hoặc dưới 70. Đây là mã để
kiểm tra điều đó: double Stoch =
iStochastic(NULL,0,KPeriod,DPeriod,Slowing,MAMethod,Price, 0,0); double LastStoch
= iStochastic(NULL,0,KPeriod,DPeriod,Slowing,MAMethod,Price,0,1); if(Stoch > 30 &&
LastStoch < 30) { // Mở lệnh mua } if(Stoch < 70 && LastStoch > 70) { // Mở lệnh bán }
Stoch là giá trị chỉ báo của thanh hiện tại, trong khi LastStoch là giá trị chỉ báo của
thanh trước đó. Nếu Stoch lớn hơn 30 và LastStoch nhỏ hơn 30, chúng ta có thể kết
luận rằng chỉ báo đã vượt lên trên mức quá bán trong thanh cuối cùng. Bằng cách đảo
ngược các toán tử so sánh, chúng ta có thể kiểm tra đường chéo gần đây bên dưới
một giá trị không đổi, chẳng hạn như mức quá mua là 70. 109 www.ZTCprep.com LẬP
TRÌNH TƯ VẤN CHUYÊN GIA Đây là một ví dụ khác sử dụng đường trung bình động.
Chúng ta sẽ tạo điều kiện chỉ mở lệnh khi FastMA và SlowMA giao nhau trong thanh
cuối cùng: double FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,0); nhân đôi LastFastMA =
iMA(NULL,0,FastMAPeriod,0,0,0,1); nhân đôi LastSlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,1); if(FastMA > SlowMA && LastFastMA <=
LastSlowMA && BuyMarketCount(Symbol(),MagicNumber) == 0) { // Mở lệnh mua }
if(FastMA < SlowMA && LastFastMA >= LastSlowMA &&
SellMarketCount(Symbol(),MagicNumber) = = 0) { // Mở lệnh bán } Trong ví dụ này,
chúng ta đang so sánh mối quan hệ của hai chỉ báo với nhau. LastFastMA và
LastSlowMA trả về giá trị trung bình động cho thanh trước đó. Nếu LastFastMA nhỏ
hơn (hoặc bằng) LastSlowMA và FastMA hiện lớn hơn SlowMA thì chúng ta biết rằng
đường trung bình động nhanh đã vượt lên trên đường trung bình động chậm trong
thanh cuối cùng. Điều này cung cấp tín hiệu giao dịch đáng tin cậy vì chúng ta có thể
giới hạn vị trí đặt lệnh của mình ở ngay sau khi xảy ra giao cắt. Bạn có thể thay đổi giá
trị Shift cho hàm LastFastMA và LastSlowMA nếu bạn muốn tăng số lượng thanh để
nhìn lại khi tìm đường chéo chỉ báo. Chúng tôi đã thêm so sánh LastFastMA và
LastSlowMA vào các điều kiện đặt hàng mua và bán trong chuyên gia cố vấn của
chúng tôi. Bây giờ chúng ta có thể loại bỏ kiểm tra MuaTicket và SellTicket vì phương
pháp này đáng tin cậy hơn so với việc kiểm tra số phiếu đặt hàng được lưu trữ. Chúng
ta cũng không phải lo lắng về việc các đơn hàng được đặt tốt sau khi xảy ra giao cắt.
Xem mã cố vấn chuyên gia trong Phụ lục C để xem tất cả các thay đổi. 110
www.ZTCprep.com Điều kiện và chỉ báo đơn hàng 111 www.ZTCprep.com LẬP TRÌNH
TƯ VẤN CHUYÊN GIA Chương 7 Làm việc với các biến thời gian và ngày tháng Trong
nội bộ, biến ngày giờ được biểu thị bằng số giây trôi qua kể từ ngày 1 tháng 1 năm
1970. Ví dụ: Ngày 15 tháng 6 năm 2009 lúc 0:00 (nửa đêm) sẽ là 1245024000. Ưu
điểm của định dạng ngày giờ là nó giúp việc so sánh thời gian trong quá khứ và tương
lai cũng như các thao tác toán học trở nên rất dễ dàng. Ví dụ: nếu bạn muốn kiểm tra
xem một ngày có trước hay sau một ngày khác, bạn sẽ thực hiện một phép toán quan
hệ đơn giản. Giả sử Ngày bắt đầu là ngày 15 tháng 6 năm 2009 lúc 14:00 và Ngày kết
thúc là ngày 16 tháng 6 năm 2009 lúc 5:00. if(StartDate < EndDate) // Kết quả là đúng
if(StartDate > EndDate) // Kết quả là sai Một ưu điểm khác là bạn có thể cộng hoặc trừ
thời gian từ một ngày cụ thể, chỉ bằng cách cộng hoặc trừ số giây thích hợp. Nếu bạn
muốn thêm 24 giờ vào StartDate, chỉ cần thêm số giây trong một ngày: datetime
AddDay = StartDate + 86400; Nếu bạn dự định thực hiện nhiều thao tác toán học với
các biến ngày giờ, có thể nên khai báo một số hằng số nguyên để biểu thị các đơn vị
thời gian nhất định: #define SEC_H1 3600 // Giây trong một giờ #define SEC_D1 86400
// Giây trong một ngày Nhược điểm của định dạng ngày giờ là nó không dễ đọc. Bạn
không thể nhìn vào một giá trị như 1245024000 và tự động cho biết rằng nó đại diện
cho ngày 15 tháng 6 năm 2009 lúc 0:00. Đối với điều này, chúng tôi sử dụng các hàm
chuyển đổi để chuyển đổi ngày giờ sang và từ dạng dễ đọc hơn. Hằng số ngày giờ
Hằng số ngày giờ là ngày và giờ được trình bày ở định dạng chuỗi sau: yyyy.mm.dd
hh:mm. Ví dụ: ngày 15 tháng 6 năm 2009 lúc 0:00 sẽ là 00:00 ngày 15 tháng 6 năm
2009. Có những cách khác có thể chấp nhận được 112 www.ZTCprep.com Làm việc
với các định dạng Ngày và Giờ cho các hằng số ngày giờ: Chủ đề tham khảo MQL Cơ
bản – Kiểu dữ liệu – Hằng số ngày giờ có nhiều thông tin hơn. Chúng tôi sẽ sử dụng
định dạng được trình bày ở trên vì đây là định dạng duy nhất có thể dễ dàng chuyển
đổi. Để chuyển đổi biến ngày giờ thành hằng số chuỗi, hãy sử dụng hàm TimeToStr().
Đây là cú pháp: string TimeToStr(datetime Time, int Output =
TIME_DATE|TIME_MINUTES); • Thời gian – Một biến ngày giờ được biểu thị bằng số
giây trôi qua kể từ ngày 1 tháng 1 năm 1970. • Đầu ra – Một tham số tùy chọn chỉ xuất
ra hằng số dưới dạng chỉ ngày, giờ và phút; giờ, phút và giây; hoặc bất kỳ sự kết hợp
nào của ngày và giờ. Các giá trị đầu vào hợp lệ là: ◦ TIME_DATE – Xuất ra ngày, ví dụ:
2009.06.15 ◦ TIME_MINUTES – Xuất ra giờ và phút, ví dụ: 05:30 ◦ TIME_SECONDS –
Xuất ra giờ, phút và giây, ví dụ: 05:30:45 Để xuất hằng số chuỗi ở định dạng
yyyy.mm.dd hh:mm mặc định, hãy để trống Đầu ra. Nếu bạn chỉ muốn ngày hoặc giờ
và phút (hoặc giây), hãy sử dụng đối số thích hợp. Trong ví dụ này, chúng tôi giả định
rằng StartTime bằng 2009.06.15 05:30:45. TimeToStr(StartTime,TIME_DATE) // Trả về
"2009.06.15" TimeToStr(StartTime,TIME_SECONDS) // Trả về "05:30:45"
TimeToStr(StartTime,TIME_MINUTES) // Trả về "05:30"
TimeToStr(StartTime,TIME_DATE| TIME_SECONDS) // Trả về "2009.06.15 05:30:45"
TimeToStr(StartTime) // Trả về "2009.06.15 05:30" Chúng ta có thể xây dựng một
hằng số datetime bằng cách nối chuỗi và chuyển đổi nó thành biến datetime bằng
hàm StrToTime(). Cú pháp giống hệt TimeToStr() ở trên nhưng không có tham số Đầu
ra. Hằng chuỗi phải ở định dạng yyyy.mm.dd hh:mm để được chuyển đổi chính xác.
Dưới đây là ví dụ về cách chúng ta có thể tập hợp hằng số ngày giờ bằng cách sử dụng
số nguyên, chuyển đổi các số nguyên đó sang định dạng chuỗi và chuyển đổi chuỗi
thành biến ngày giờ. Đầu tiên, chúng ta sẽkhai báo một số biến ngoài để đặt ngày giờ:
extern int UseMonth = 6; extern int UseDay = 15; extern int UseHour = 5; extern int
Ấ
UseMinute = 30; 113 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Tiếp theo,
chúng ta tạo hằng chuỗi bằng cách sử dụng hàm StringConcatenate() và cuối cùng
chuyển chuỗi sang định dạng datetime bằng StrToTime(). chuỗi DateConstant =
StringConcatenate(Year(),".",UseMonth,".",UseDay," ", UseHour,">,UseMinute); //
DateConstant là "2009.6.15 05:30" datetime StartTime = StrToTime(DateConstant); //
StartTime là "1245043800" Lưu ý rằng trong hàm StringConcatenate(), chúng ta sử
dụng Year() để trả về năm hiện tại thay vì sử dụng biến ngoài. Bạn có thể sử dụng các
hàm như Tháng(), Ngày(), v.v. để chèn các giá trị thời gian hiện tại. Chúng tôi sẽ đề cập
đến những điều này trong phần tiếp theo. Hàm ngày và giờ Có hai hàm trả về thời gian
hiện tại: TimeCurrent() trả về thời gian hiện tại của máy chủ, trong khi TimeLocal() trả
về thời gian trên máy tính cục bộ của bạn. Bạn có thể sử dụng bất cứ điều gì bạn thích.
Bạn có thể muốn tạo một biến ngoài boolean để chọn giữa hai biến: extern bool
UseLocalTime = true; Đây là mã để gán giờ địa phương hiện tại hoặc thời gian hiện tại
của máy chủ cho một biến có tên CurrentTime. if(UseLocalTime == true) datetime
CurrentTime = TimeLocal(); // Giờ địa phương khác CurrentTime = TimeCurrent(); //
Giờ máy chủ Đôi khi bạn có thể chỉ cần truy xuất một phần thời gian hiện tại, chẳng
hạn như giờ hoặc ngày. Đây là danh sách các hàm hữu ích nhất mà bạn có thể sử
dụng để trả về giá trị thời gian hiện tại. Tất cả các chức năng này sử dụng thời gian
của máy chủ – không phải thời gian trên máy tính cục bộ của bạn. Giá trị trả về có kiểu
số nguyên: • Year() – Năm hiện tại có bốn chữ số, ví dụ: 2009. • Tháng() – Tháng hiện
tại trong năm từ 1 đến 12. • Day() – Ngày hiện tại của tháng từ 1 đến 31. •
DayOfWeek() – Một số nguyên biểu thị ngày hiện tại trong tuần. Chủ Nhật là 0, Thứ Hai
là 1, Thứ Sáu là 5, v.v. • Hour() – Giờ hiện tại trong 24 giờ, từ 0 đến 23. Ví dụ: 3 giờ sáng
là 3 và 3 giờ chiều là 15. • Minute() – Phút hiện tại từ 0 đến 59. 114 www.ZTCprep.com
Working với Thời gian và Ngày Bạn cũng có thể truy xuất các giá trị này từ bất kỳ biến
ngày giờ nào bằng cách sử dụng một bộ hàm khác. Các hàm này yêu cầu biến
datetime làm tham số duy nhất, nhưng nếu không thì hoạt động giống như các hàm ở
trên. Nếu bạn muốn truy xuất giá trị thời gian từ TimeLocal(), hãy sử dụng đầu ra của
hàm TimeLocal() làm đối số cho các hàm bên dưới: • TimeYear() – Năm có bốn chữ
số của giá trị ngày giờ được chỉ định. • TimeMonth() – Tháng của giá trị datetime được
chỉ định từ 1 đến 12. • TimeDay() – Ngày trong tháng của giá trị datetime được chỉ
định từ 1 đến 31. • TimeDayOfWeek() – Một số nguyên biểu thị ngày trong tuần của
giá trị ngày giờ được chỉ định. Chủ Nhật là 0, Thứ Hai là 1, Thứ Sáu là 5, v.v. •
TimeHour() – Giờ của giá trị ngày giờ được chỉ định trong thời gian 24 giờ, từ 0 đến 23.
• TimeMinute() – Phút của giá trị ngày giờ được chỉ định từ 0 đến 59. Dưới đây là một
vài ví dụ về cách sử dụng các hàm này . Giả sử rằng TimeLocal() bằng 2009.06.15
05:30. ngày giờ CurrentTime = TimeLocal(); int GetMonth = TimeMonth(CurrentTime);
// Trả về 6 int GetHour = TimeHour(CurrentTime); // Trả về 5 int GetWeekday =
TimeDayOfWeek(CurrentTime); // Trả về 1 cho Thứ Hai Tạo một bộ đếm thời gian đơn
giản Một điều rất hữu ích mà chúng ta có thể làm với thời gian và ngày tháng trong
MQL là thêm bộ đếm thời gian cho chuyên gia cố vấn của chúng ta. Một số nhà giao
dịch muốn giới hạn giao dịch của họ trong những giờ hoạt động tích cực nhất trong
ngày, chẳng hạn như các phiên giao dịch ở London và New York. Những người khác có
thể muốn tránh giao dịch trong các sự kiện thị trường biến động, chẳng hạn như các
bản tin và NFP. Để xây dựng bộ đếm thời gian, chúng ta cần chỉ định thời gian bắt đầu
và thời gian kết thúc. Chúng ta sẽ sử dụng các biến số nguyên bên ngoài để nhập các
tham số thời gian. Chúng ta sẽ tạo một chuỗi hằng số datetime và chuyển chuỗi đó
thành biến datetime. Sau đó, chúng tôi sẽ so sánh thời gian bắt đầu và kết thúc với
thời điểm hiện tại. Nếu thời gian hiện tại lớn hơn thời gian bắt đầu nhưng nhỏ hơn thời
gian kết thúc thì giao dịch sẽ được phép. Dưới đây là các biến bên ngoài mà chúng tôi
sẽ sử dụng. Chúng tôi sẽ đặt một biến để bật và tắt bộ hẹn giờ cũng như chọn thời
gian hiện tại (máy chủ hoặc cục bộ). Chúng tôi có cài đặt tháng, ngày, giờ và phút cho
cả thời gian bắt đầu và kết thúc: 115 www.ZTCprep.com CHƯƠNG TRÌNH TƯ VẤN
CHUYÊN NGHIỆP LẬP TRÌNH extern bool UseTimer = true; bool bên ngoài
UseLocalTime = false; extern int StartMonth = 6; extern int Ngày bắt đầu = 15; extern
int StartHour = 7; extern int StartMinute = 0; extern int EndMonth = 6; extern int EndDay
= 15; extern int EndHour = 2; extern int EndMinute = 30; Và đây là đoạn code để kiểm
tra xem có cho phép giao dịch hay không. Biến TradeAllowed xác định có nên mở giao
dịch mới hay không. Nếu UseTimer được đặt thành false thì TradeAllowed sẽ tự động
được đặt thành true. Mặt khác, chúng tôi đánh giá thời gian bắt đầu và kết thúc của
mình so với thời gian hiện tại để xem liệu chúng tôi có cho phép giao dịch hay không.
if(UseTimer == true) { // Chuyển đổi chuỗi thời gian bắt đầu StartConstant =
StringConcatenate(Year(),".",StartMonth,".",StartDay," ", StartHour,://,StartMinute); ngày
giờ StartTime = StrToTime(StartConstant); if(StartMonth == 12 && StartDay == 31 &&
EndMonth == 1) int EndYear = Year() + 1; khác Cuối năm = Năm(); // Chuyển đổi chuỗi
thời gian kết thúc EndConstant = StringConcatenate(EndYear,".",EndMonth,".",EndDay," ",
EndHour,">,EndMinute); ngày giờ EndTime = StrToTime(EndConstant); // Chọn thời
gian cục bộ hoặc máy chủ if(UseLocalTime == true) datetime CurrentTime =
TimeLocal(); khác CurrentTime = TimeCurrent(); // Kiểm tra điều kiện giao dịch
if(StartTime <= CurrentTime && EndTime > CurrentTime) { bool TradeAllowed = true; }
else TradeAllowed = false; } else TradeAllowed = true; 116 www.ZTCprep.com Làm
việc với Thời gian và Ngày Chúng ta bắt đầu bằng cách chuyển đổi thời gian bắt đầu
thành một biến ngày giờ, StartTime. Câu lệnh if(StartMonth == 12 && StartDay == 31
&& EndMonth == 1) kiểm tra xem liệu ngày bắt đầu có phải là ngày cuối cùng của năm
hay không và liệu ngày kết thúc có sau ngày đầu tiên của năm tiếp theo hay không.
Nếu vậy, nó sẽ tự động tăng năm cuối lên 1. Nếu không, chúng tôi sử dụng năm hiện
tại cho EndYear. Tiếp theo, chúng tôi chuyển đổi thời gian kết thúc thành biến ngày giờ
EndTime và chọn Thời gian hiện tại mà chúng tôi muốn sử dụng, máy chủ hoặc cục
bộ. Khối if cuối cùng kiểm tra xem thời gian hiện tại có nằm giữa thời gian bắt đầu và
kết thúc hay không. Nếu vậy, TradeAllowed được đặt thành true. Bây giờ chúng ta cần
thêm mã để kiểm soát việc thực hiện giao dịch. Cách dễ nhất để làm điều này là thêm
một khối if xung quanh quy trình mở lệnh của chúng ta: // Bắt đầu khối giao dịch
if(TradeAllowed == true) { // Buy Order if(FastMA > SlowMA && BuyTicket == 0 &&
BuyOrderCount(Symbol( ),MagicNumber) == 0) { // Bỏ qua mã lệnh mua để cho ngắn
gọn } // Lệnh bán if(FastMA < SlowMA && SellTicket == 0 &&
SellOrderCount(Symbol(),MagicNumber) == 0) { // Lệnh bán mã được bỏ qua để ngắn
gọn } } // Kết thúc khối giao dịch Có nhiều cách khác để tạo bộ tính giờ - ví dụ: bạn có
thể sử dụng ngày trong tuần thay vì tháng và ngày hoặc đặt thời gian giao dịch tương
ứng với ngày hiện tại. Chúng tôi sẽ để bạn, người đọc, tự tạo một bộ đếm thời gian phù
hợp với nhu cầu của bạn. Thực thi trên thanh mở Theo mặc định, chuyên gia tư vấn
chạy trong thời gian thực, trên từng tích tắc. Nhưng trong một số trường hợp, tốt hơn
là chỉ nên kiểm tra điều kiện giao dịch một lần trên mỗi thanh. Bằng cách đợi thanh
hiện tại đóng lại, chúng ta có thể chắc chắn rằng điều kiện đã xảy ra và tín hiệu đó hợp
lệ. Ngược lại, bằng cách thực hiện giao dịch trong thời gian thực, chúng ta có thể dễ bị
tín hiệu sai hơn. Giao dịch một lần trên mỗi thanh cũng có nghĩa là kết quả trong Trình
kiểm tra chiến lược sẽ chính xác và phù hợp hơn. Do những hạn chế cố hữu của Công
cụ kiểm tra chiến lược của MetaTrader, việc sử dụng "Mỗi dấu tích" làm mô hình thử
nghiệm sẽ tạo ra kết quả kiểm tra ngược không đáng tin cậy, do thực tế là các dấu tích
thường được mô hình hóa từ dữ liệu M1. Các giao dịch xảy ra trong giao dịch trực tiếp
sẽ không nhất thiết phải tương ứng với giao dịch được thực hiện trong Trình kiểm tra
chiến lược. Nhưng bằng cách đặt các giao dịch của chúng tôi ở trạng thái đóng trên
thanh và sử dụng "Chỉ giá mở" làm mô hình thử nghiệm, chúng tôi có thể nhận được
kết quả thử nghiệm phản ánh chính xác hơn các giao dịch theo thời gian thực. Nhược
điểm của việc giao dịch một lần trên mỗi thanh là giao dịch có thể được thực hiện
muộn, đặc biệt nếu có nhiều biến động giá trong suốt thanh. Về cơ bản, đó là sự đánh
đổi giữa khả năng đáp ứng và độ tin cậy. Để kiểm tra các điều kiện giao dịch một lần
cho mỗi thanh, chúng ta phải kiểm tra dấu thời gian của thanh hiện tại. Chúng tôi sẽ
lưu dấu thời gian này vào một biến toàn cục. Sau mỗi lần thực hiện của cố vấn chuyên
gia, chúng tôi sẽ so sánh dấu thời gian đã lưu với dấu thời gian hiện tại. Khi dấu thời
gian của thanh hiện tại thay đổi, cho biết rằng một thanh mới đã được mở, chúng tôi
sẽ kiểm tra các điều kiện giao dịch. Chúng ta cũng phải điều chỉnh tham số dịch
chuyển của các hàm chỉ báo, hàm giá và mảng để trả về giá trị của thanh trước đó.
Nếu chức năng chỉ báo hoặc mảng giá được đặt để kiểm tra thanh hiện tại, chúng tôi
sẽ dịch chuyển chỉ số thanh đi 1 để kiểm tra thanh trước đó. Tất cả các chỉ báo và
mảng giá phải tăng tham số dịch chuyển thêm 1. Về mặt kỹ thuật, chúng tôi đang kiểm
tra điều kiện giao dịch trên tích tắc đầu tiên của thanh mới, đồng thời kiểm tra giá trị
đóng của thanh trước đó. Chúng tôi không kiểm tra thanh hiện đang mở khi thực hiện
một lần trên mỗi thanh. Đây là mã để kiểm tra việc mở một thanh mới. Đầu tiên, chúng
ta khai báo một biến ngoài có tên CheckOncePerBar để bật và tắt tính năng này. Sau
đó, chúng ta khai báo một biến toàn cục datetime để lưu dấu thời gian của thanh hiện
tại – đây sẽ là CurrentTimeStamp. Trong hàm init(), chúng ta sẽ gán dấu thời gian của
thanh hiện tại cho CurrentTimeStamp. Điều này sẽ trì hoãn việc kiểm tra điều kiện giao
dịch cho đến khi mở thanh tiếp theo: // Biến bên ngoài extern bool CheckOncePerBar =
true; // Biến toàn cục datetime CurrentTimeStamp; // Hàm khởi tạo int init() {
CurrentTimeStamp = Thời gian[0]; } 118 www.ZTCprep.com Làm việc với Thời gian và
Ngày tháng Đây là đoạn mã bắt đầu hàm start() của chúng ta, ngay sau bộ đếm thời
gian. Biến số nguyên BarShift sẽ xác định xem nên đặt giá trị Shift của các hàm chỉ
báo và giá của chúng tôi cho thanh hiện tại hay thanh trước đó. Biến boolean NewBar
sẽ xác định xem chúng ta có kiểm tra các điều kiện giao dịch hay không:
if(CheckOncePerBar == true) { int BarShift = 1; if(CurrentTimeStamp != Time[0]) {
CurrentTimeStamp = Time[0]; bool NewBar = đúng; } khác NewBar = false; } khác {
NewBar = true; BarShift = 0; } Nếu CheckOncePerBar được đặt thành true, trước tiên
chúng tôi sẽ đặt BarShift thành 1. Điều này sẽ đặt tham số Shift của tất cả các
hàm/mảng chỉ báo và giá thành thanh trước đó. Tiếp theo, chúng ta so sánh giá trị của
biến CurrentTimeStamp với Time[0], là dấu thời gian của thanh hiện tại. Nếu hai giá trị
không khớp nhau, chúng tôi sẽ gán giá trị của Time[0] cho CurrentTimeStamp và đặt
NewBar thành true. Các điều kiện giao dịch sẽ được kiểm tra ngay sau đó. Trong các
lần chạy tiếp theo, CurrentTimeStamp và Time[0] sẽ khớp nhau, điều đó có nghĩa là
NewBar sẽ được đặt thành sai. Các điều kiện giao dịch sẽ không được kiểm tra cho
đến khi một thanh mới mở ra. Khi một thanh mới mở ra, Time[0] sẽ có giá trị khác với
CurrentTimeStamp và NewBar sẽ được đặt thành true một lần nữa. Nếu
CheckOncePerBar được đặt thành false, NewBar sẽ tự động được đặt thành true và
BarShift sẽ được đặt thành 0. Thao tác này sẽ kiểm tra các điều kiện giao dịch trên
mỗi tích tắc, như trước đây. Biến BarShift sẽ cần được gán cho tham số Shift của bất
kỳ hàm chỉ báo, hàm giá hoặc mảng nào tham chiếu đến thanh gần đây nhất. Dưới đây
là một số ví dụ về cách áp dụng điều này: double FastMA =
iMA(NULL,0,FastMAPeriod,0,0,0,BarShift); if(Close[BarShift] > Open[BarShift]) double
Ố Ấ
UseLow = iLow(NULL,0,BarShift); 119 www.ZTCprep.com CHƯƠNG TRÌNH CỐ VẤN
CHUYÊN GIA Bạn nên nhận ra những ví dụ này từ trước. Thay vì kiểm tra thanh hiện tại,
chúng ta sẽ kiểm tra thanh vừa đóng, tức là thanh trước đó. Nếu bạn cần tham chiếu
một thanh trước thanh đóng cuối cùng, chỉ cần thêm tham số shift hiện tại vào
BarShift: double LastFastMA = iMA(NULL,0,FastMAPeriod,0,0,0,BarShift+1); Nếu bạn
không dự đoán sẽ cần chạy Expert Advisor một lần cho mỗi thanh, bạn sẽ không cần
thêm mã này. Nhưng đối với nhiều hệ thống giao dịch dựa trên chỉ báo, điều này có thể
làm cho kết quả giao dịch và kiểm tra ngược của bạn trở nên đáng tin cậy hơn. Để
kiểm soát việc thực hiện giao dịch, chúng ta cần kiểm tra giá trị của NewBar trước quy
trình đặt lệnh. Chúng ta có thể thực hiện việc này bằng cách sử dụng khối if mà chúng
ta đã đặt trước đó cho bộ đếm thời gian: // Bắt đầu khối giao dịch if(TradeAllowed ==
true && NewBar == true) { // Buy Order if(FastMA > SlowMA && BuyTicket == 0 &&
BuyOrderCount( Symbol(),MagicNumber) == 0) { // Bỏ qua mã lệnh mua để cho ngắn
gọn } // Lệnh bán if(FastMA < SlowMA && SellTicket == 0 &&
SellOrderCount(Symbol(),MagicNumber) == 0) { // Mã lệnh bán được bỏ qua để ngắn
gọn } } // Kết thúc khối giao dịch 120 www.ZTCprep.com Làm việc với Thời gian và
Ngày 121 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA Chương 8 Mẹo và thủ
thuật Trong chương này, chúng tôi sẽ đề cập đến các tính năng bổ sung có thể hữu ích
trong các chuyên gia cố vấn của bạn. Ký tự thoát Nếu bạn muốn thêm dấu ngoặc kép
hoặc ký tự dấu gạch chéo ngược vào hằng chuỗi, bạn sẽ cần thoát ký tự bằng dấu
gạch chéo ngược (\). Ví dụ: nếu bạn cần chèn một dấu trích dẫn kép, ký tự thoát sẽ là
\". Đối với một dấu ngoặc đơn, ký tự thoát là \'. Đối với dấu gạch chéo ngược, hãy sử
dụng hai dấu gạch chéo ngược làm ký tự thoát: \\ string EscQuotes = " Chuỗi này có
\"thoát dấu ngoặc kép\""; // Đầu ra: Chuỗi này có "dấu ngoặc kép thoát" string
EscQuote = "Chuỗi này có \'thoát dấu ngoặc đơn\'"; // Đầu ra: Chuỗi này có 'thoát dấu
ngoặc đơn quote' string EscSlash = "Chuỗi này có dấu gạch chéo ngược thoát \\"; //
Đầu ra: Chuỗi này có dấu gạch chéo ngược thoát \ Nếu bạn cần một chuỗi trải dài trên
nhiều dòng, hãy sử dụng ký tự thoát \n để thêm dòng mới: string NewLine = "Chuỗi này
có \na dòng mới"; // Kết quả: Chuỗi này có dòng mới Sử dụng Chart Comments Bạn có
thể in văn bản ở góc trên bên trái của biểu đồ bằng hàm Comment(). Hàm này có thể
được sử dụng để in thông tin trạng thái , cài đặt chỉ báo hoặc bất kỳ thông tin nào
khác mà bạn có thể thấy hữu ích. Một phương pháp để hiển thị nhận xét trên biểu đồ
là khai báo một số biến chuỗi và nối chúng lại với nhau bằng các ký tự dòng mới. Một
chuỗi có thể được sử dụng để hiển thị cài đặt, một chuỗi khác để hiển thị thông báo
thông tin hoặc trạng thái đơn hàng, v.v. Chuỗi được nối sẽ được chuyển đến hàm
Comment(). Đặt hàm Comment() ở cuối hàm start() để cập nhật nhận xét trên biểu đồ:
122 www.ZTCprep.com Mẹo và thủ thuật chuỗi settingsComment = "FastMAPeriod:
"+FastMAPeriod+" SlowMAPeriod: "+SlowMAPeriod; string StatusComment = "Đặt
lệnh mua"; Nhận xét(SettingsComment+"\n"+StatusComment); Chúng ta khai báo và
đặt giá trị của chuỗi SettingComment và StatusComment bên trong hàm start(). Khi
kết thúc hàm start, chúng ta gọi hàm Comment() và sử dụng nó để in các nhận xét
của chúng ta lên biểu đồ. Chúng tôi sử dụng ký tự dòng mới (\n) để tách các nhận xét
thành hai dòng. Hình 8.1: Nhận xét biểu đồ bằng ký tự dòng mới Kiểm tra cài đặt Có
một số thuộc tính cố vấn chuyên gia phải được bật trước khi cố vấn chuyên gia có thể
được phép giao dịch. Các cài đặt này nằm trong tab Chung trong hộp thoại Thuộc tính
Chuyên gia. Cài đặt Cho phép giao dịch trực tiếp phải được bật trước khi giao dịch có
thể bắt đầu. Nếu tính năng này không được bật, khuôn mặt cau mày sẽ xuất hiện ở
góc trên bên phải của biểu đồ, bên cạnh tên chuyên gia cố vấn. Bạn có thể kiểm tra
tình trạng này trong EA của mình bằng cách sử dụng hàm IsTradeAllowed(). Nếu trả về
sai thì cài đặt Cho phép giao dịch trực tiếp sẽ bị tắt. Nếu bạn muốn hiển thị thông báo
cho người dùng cho biết rằng cài đặt này cần được kích hoạt, bạn có thể thực hiện
như sau: if(IsTradeAllowed() == false) Alert("Bật cài đặt \'Cho phép giao dịch trực
tiếp\' trong Thuộc tính chuyên gia!"); Nếu chuyên gia cố vấn của bạn sử dụng thư viện
.ex4 bên ngoài thì cài đặt Cho phép nhập chuyên gia bên ngoài phải được bật trong
Thuộc tính chuyên gia. Bạn có thể kiểm tra điều này bằng cách sử dụng hàm
IsLibrariesAllowed(): if(IsLibrariesAllowed() == false) Alert("Bật cài đặt \'Cho phép
nhập chuyên gia bên ngoài\' trong Thuộc tính chuyên gia!"); Điều tương tự có thể được
thực hiện đối với các tệp DLL bằng cách sử dụng hàm IsDllsAllowed():
if(IsDllsAllowed() == false) Alert("Bật cài đặt \'Cho phép nhập DLL\' trong Thuộc tính
Chuyên gia!"); 123 www.ZTCprep.com LẬP TRÌNH EXPERT Advisor Hình 8.2 – Tab
chung của hộp thoại Thuộc tính Expert Advisor. Bạn có thể xem tất cả các chức năng
kiểm tra thiết bị đầu cuối trong Tham chiếu MQL trong phần Kiểm tra. Giới hạn về bản
demo hoặc tài khoản Bạn có thể quyết định bán cố vấn chuyên gia có lợi nhuận của
mình cho các nhà giao dịch khác tại một thời điểm nào đó. Bạn cũng có thể muốn
cung cấp phiên bản demo để người mua tiềm năng thử nghiệm. Để ngăn EA của bạn
bị những người không được ủy quyền phân phối hoặc giao dịch tự do, bạn sẽ muốn kết
hợp một số loại giới hạn tài khoản nhằm hạn chế việc sử dụng EA đối với những người
mua được ủy quyền. Bạn thậm chí có thể muốn giới hạn việc sử dụng đối với một nhà
môi giới cụ thể. Để giới hạn việc sử dụng tài khoản demo, hãy sử dụng hàm IsDemo()
để kiểm tra xem tài khoản hiện đang hoạt động có phải là tài khoản demo hay không.
Nếu tài khoản hiện tại không phải là tài khoản demo, chúng tôi sẽ hiển thị cảnh báo và
tạm dừng việc thực thi EA. if(IsDemo() == false) { Alert("EA này chỉ dành cho tài khoản
demo!"); trở lại (0); } 124 www.ZTCprep.com Mẹo và thủ thuật Bạn có thể sử dụng các
hàm tài khoản AccountName(), AccountNumber() và AccountBroker() để kiểm tra tên
tài khoản, số tài khoản và nhà môi giới tương ứng. Giới hạn mức sử dụng theo số tài
khoản là phương pháp bảo vệ phổ biến và dễ thực hiện: int CustomerAccount =
123456; if(AccountNumber() != CustomerAccount) { Alert("Số tài khoản không khớp!");
trở lại (0); } Bạn có thể sử dụng AccountName() hoặc AccountBroker() theo cách
tương tự. Đối với AccountBroker(), trước tiên bạn cần sử dụng câu lệnh Print() để truy
xuất giá trị trả về chính xác từ nhà môi giới. Giá trị này sẽ được in trong nhật ký chuyên
gia. Nếu bạn quyết định bán EA cho mục đích thương mại, hãy lưu ý rằng các tệp MQL
nổi tiếng là dễ dịch ngược. Có nhiều phương pháp khác nhau mà bạn có thể sử dụng
để gây khó khăn hơn cho tin tặc trong việc bẻ khóa EA của bạn, chẳng hạn như đặt
các chức năng trong thư viện bên ngoài hoặc tệp DLL. Nhưng cuối cùng, có rất ít sự
bảo vệ chống lại một kẻ bẻ khóa kiên quyết. MessageBox() Cho đến nay trong cuốn
sách này, chúng ta đã sử dụng hàm Alert() có sẵn để hiển thị các thông báo lỗi. Nhưng
nếu bạn muốn tùy chỉnh hộp thoại cảnh báo hoặc yêu cầu người dùng nhập thông tin
thì sao? Hàm MessageBox() sẽ cho phép bạn tạo hộp thoại bật lên tùy chỉnh bằng
cách sử dụng các hàm API của Windows. Để sử dụng hàm MessageBox(), trước tiên
chúng ta phải #include tệp WinUser32.mqh được cài đặt với MetaTrader. Tệp này nhập
các hàm từ tệp user32.dll của Windows và xác định các hằng số cần thiết để hàm
MessageBox() hoạt động. Đây là cú pháp của hàm MessageBox(): int
MessageBox(string Text, string Title, int Flags); Để sử dụng hàm MessageBox(), chúng
ta phải xác định Văn bản xuất hiện trong hộp thoại bật lên, cùng với Tiêu đề xuất hiện
trên thanh tiêu đề. Chúng tôi cũng sẽ cần chỉ định Cờ cho biết nút và biểu tượng nào
sẽ xuất hiện trong cửa sổ bật lên của chúng tôi. Nếu không có cờ nào được chỉ định,
nút OK sẽ là mặc định. Các cờ phải được phân tách bằng ký tự ống (|). Dưới đây là ví
dụ về hộp thông báo có nút Có/Không và biểu tượng dấu chấm hỏi: 125
Ấ
www.ZTCprep.com CHƯƠNG TRÌNH TƯ VẤN CHUYÊN NGHIỆP // Chỉ thị tiền xử lý
#include // start() function int YesNoBox = MessageBox("Đặt giao dịch?","Xác nhận
giao dịch", MB_YESNO|MB_ICONQUESTION); if(YesNoBox == IDYES) { // Đặt hàng } Cờ
MB_YESNO chỉ định rằng chúng tôi sẽ sử dụng các nút Có/Không trong hộp thông báo
của mình, trong khi cờ MB_ICONQUESTION đặt biểu tượng dấu chấm hỏi trong hộp
thoại. Biến số nguyên YesNoBox giữ giá trị trả về của hàm MessageBox(), giá trị này
sẽ cho biết nút nào đã được nhấn. Nếu nhấn nút Có, giá trị của YesNoBox sẽ là IDYES
và đơn hàng sẽ được đặt. Nếu nhấn nút No Hình 8.3 – Hộp thoại Popup được tạo
bằng nút này thì cờ quay lại sẽ là IDNO. Bạn có thể sử dụng hàm MessageBox() sử
dụng giá trị trả về của MessageBox() làm đầu vào để xác định tiến trình hành động,
chẳng hạn như đặt hàng. Sau đây là danh sách một phần các cờ để sử dụng trong hộp
thư của bạn. Để có danh sách đầy đủ, vui lòng xem chủ đề Tham khảo MQL Hằng số
tiêu chuẩn – MessageBox. Cờ nút Những cờ này chỉ định nút nào xuất hiện trong hộp
thông báo của bạn. • MB_OKCANCEL – Nút OK và Hủy. • MB_YESNO – Nút Có và
Không. • MB_YESNOCANCEL – Nút Có, Không và Hủy. 126 www.ZTCprep.com Mẹo và
thủ thuật Cờ biểu tượng Những cờ này chỉ định các biểu tượng xuất hiện bên cạnh văn
bản trong hộp thông báo. • MB_ICONSTOP – Biểu tượng biển báo dừng. •
MB_ICONQUESTION – Biểu tượng dấu chấm hỏi. • MB_ICONEXCLAMATION – Biểu
tượng dấu chấm than. • MB_ICONINFORMATION – Biểu tượng thông tin. Cờ trả về
Những cờ này là giá trị trả về của hàm MessageBox() và cho biết nút nào đã được
nhấn. • IDOK – Nút OK đã được nhấn. • IDCANCEL – Nút Hủy đã được nhấn. • IDYES –
Nút Yes đã được nhấn • IDNO – Nút No đã được nhấn. Cảnh báo qua Email Cố vấn
chuyên gia của bạn có thể thông báo cho bạn qua email về các giao dịch đã đặt, thiết
lập giao dịch tiềm năng và hơn thế nữa. Hàm SendMail() sẽ gửi email có chủ đề và nội
dung bạn chọn đến địa chỉ email được liệt kê trong hộp thoại Công cụ – Tùy chọn
trong tab Email. Trong tab Email, trước tiên bạn phải chỉ định máy chủ thư SMTP có số
cổng – ví dụ: mail.yourdomain.com:25 -- cùng với tên người dùng và mật khẩu, nếu
được yêu cầu. Kiểm tra với ISP hoặc nhà cung cấp dịch vụ lưu trữ của bạn để biết
thông tin này. Bạn có thể sử dụng bất kỳ địa chỉ email nào trong trường Từ. Trường
Đến là địa chỉ email để gửi tin nhắn đến. Đảm bảo kiểm tra cài đặt Bật ở trên cùng để
bật gửi tin nhắn. Hàm SendMail() có hai đối số: đối số đầu tiên là dòng chủ đề của
email và đối số thứ hai là nội dung của chính email đó. Bạn có thể sử dụng dòng mới,
ký tự thoát, biến và hằng trong nội dung email của mình. 127 www.ZTCprep.com LẬP
TRÌNH TƯ VẤN CHUYÊN GIA Hình 8.4 – Cài đặt Email trong Công cụ – Tùy chọn. Đây
là một ví dụ về cách sử dụng SendMail(): string EmailSubject = "Buy order order";
string EmailBody = "Mua lệnh "+Ticket+" đặt trên "+Symbol()+" tại "+Ask; // Đầu ra
mẫu: "Lệnh mua 12584 đặt trên EURDUSD với giá 1,4544"
SendMail(EmailSubject,EmailBody); Thử lại khi gặp lỗi Trong suốt cuốn sách này,
chúng tôi đã cố gắng xác minh các thông số đặt hàng trước khi thử đặt hàng, để tránh
các thông báo lỗi phổ biến do cài đặt hoặc giá không chính xác. Tuy nhiên, lỗi vẫn có
thể xảy ra do báo giá lại, bối cảnh giao dịch bận hoặc sự cố máy chủ. Không phải lúc
nào cũng có thể tránh được những lỗi này nhưng chúng tôi có thể thử đặt lại đơn hàng
khi điều này xảy ra. Để thử lại một đơn hàng khi có lỗi, chúng ta sẽ đặt hàm
OrderSend() bên trong vòng lặp while. Nếu OrderSend() không trả lại số vé, chúng tôi
sẽ thử lại đơn hàng: 128 www.ZTCprep.com Mẹo và thủ thuật int Ticket = 0;
while(Ticket <= 0) { Ticket =
OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit); } Đầu tiên chúng ta khai báo biến số vé, trong trường hợp
này là Vé. Miễn là Ticket không lớn hơn 0, vòng lặp while với hàm OrderSend() sẽ thực
hiện lặp đi lặp lại. Tuy nhiên, có một vấn đề với vòng lặp này. Trong trường hợp xảy ra
lỗi mã hóa hoặc một số lỗi giao dịch chưa được sửa khác, vòng lặp sẽ lặp lại vô thời
hạn và chuyên gia cố vấn của bạn sẽ bị treo. Chúng ta có thể giảm bớt điều này bằng
cách thêm số lần thử lại tối đa: int Retries = 0; int MaxRetries = 5; int Vé = 0;
while(Ticket <= 0) { Ticket =
OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,BuyStopLoss,
BuyTakeProfit); if(Thử lại <= MaxRetries) Thử lại++; nếu không thì phá vỡ; } Chúng tôi
khai báo một biến để sử dụng làm bộ đếm số lần thử lại (Thử lại) và cài đặt số lần thử
tối đa (MaxRetries). Miễn là chúng tôi chưa vượt quá MaxRetries, biến Retries sẽ tăng
lên và vòng lặp sẽ lặp lại. Ngay khi đạt đến MaxRetries, toán tử ngắt sẽ kết thúc vòng
lặp. Sau đó, bạn có thể cảnh báo người dùng về tình trạng lỗi nếu cần. Nếu bạn muốn
làm cho vòng lặp thử lại phụ thuộc vào một điều kiện lỗi cụ thể, chúng tôi có thể kiểm
tra mã lỗi dựa trên danh sách và trả về giá trị true nếu có kết quả khớp. Hàm này chứa
một số mã lỗi phổ biến cho biết điều kiện có thể thử lại giao dịch thành công: bool
ErrorCheck(int ErrorCode) { switch(ErrorCode) { case 128: // Trade timeout
return(true); case 136: // Tắt dấu ngoặc kép return(true); 129 www.ZTCprep.com
CHUYÊN GIA LẬP TRÌNH TƯ VẤN trường hợp 138: // Báo giá lại return(true); case 146:
// Bối cảnh giao dịch bận return(true); mặc định: trả về (sai); } } Hàm này sử dụng toán
tử switch. Chúng tôi đang tìm kiếm một nhãn chữ hoa có giá trị khớp với biểu thức
được gán cho toán tử chuyển đổi (trong ví dụ này là ErrorCode). Nếu tìm thấy trường
hợp phù hợp, mã sau trường hợp sẽ được thực thi. Nếu không có nhãn chữ hoa chữ
thường nào khớp thì mã sau nhãn mặc định sẽ được thực thi. Khi tìm thấy trường hợp
khớp, khối chuyển đổi phải được thoát bằng toán tử break hoặc return. Trong ví dụ này,
chúng tôi đang sử dụng toán tử trả về để trả về giá trị đúng/sai cho hàm gọi. Toán tử
chuyển đổi có thể hữu ích trong việc đánh giá sự trùng khớp của một hằng số nguyên,
nhưng tiện ích của nó khá hạn chế. Đây là cách chúng tôi sử dụng ErrorCheck() để thử
lại một vị trí đặt hàng có điều kiện: int Retries; int MaxRetries = 5; vé int; while(Ticket
<= 0) { Ticket =
OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,BuyStopLoss,
BuyTakeProfit); if(Ticket == -1) int ErrCode = GetLastError(); if(Thử lại <= MaxRetries
&& ErrorCheck(ErrCode) == true) Thử lại++; nếu không thì phá vỡ; } Nếu Vé trả về -1,
cho biết đã xảy ra lỗi, chúng tôi truy xuất mã lỗi bằng GetLastError(). Chúng ta chuyển
mã lỗi tới hàm ErrorCheck() ở trên. Nếu mã lỗi khớp với bất kỳ lỗi nào trong hàm kiểm
tra lỗi, ErrorCheck() sẽ trả về true và hàm OrderSend() sẽ được thử lại tối đa 5 lần. 130
www.ZTCprep.com Mẹo và thủ thuật sử dụng nhận xét đơn hàng làm mã định danh
Chúng tôi đã sử dụng "con số kỳ diệu" làm mã định danh đơn hàng giúp xác định duy
nhất các đơn hàng được đặt bởi một cố vấn chuyên gia cụ thể. Nếu cố vấn chuyên
môn của bạn đặt nhiều đơn hàng cùng một lúc và bạn muốn có thể xử lý từng đơn
hàng đó một cách khác nhau, bạn có thể sử dụng nhận xét đơn hàng làm mã định
danh tùy chọn. Ví dụ: giả sử chuyên gia cố vấn của bạn sẽ đặt hai loại đơn đặt hàng.
Bạn muốn có thể sửa đổi hoặc đóng các lệnh này một cách riêng biệt. Bạn sẽ muốn
sử dụng hai hàm OrderSend() và đặt một nhận xét đơn hàng khác nhau cho mỗi hàm.
Sau đó, khi chọn các lệnh sử dụng vòng lặp lệnh ở chương 5, bạn sẽ sử dụng
OrderComment() làm một trong những điều kiện để xác định các lệnh cần sửa đổi
hoặc đóng. string OrderComment1 = "Đơn hàng đầu tiên"; string OrderComment2 =
"Thứ tự thứ hai"; // Vị trí đặt hàng int Ticket1 =
OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,BuyStopLoss,
BuyTakeProfit,OrderComment1,MagicNumber,0,Green); int Ticket2 =
OrderSend(Symbol(),OP_BUY,LotSize,Openprice,UseSlippage,BuyStopLoss,
BuyTakeProfit,OrderComment2,MagicNumber,0,Green); // Sửa đổi thứ tự for(int
Counter = 0; Counter <= OrderTotal()-1; Counter++) {
OrderSelect(Counter,SELECT_BY_POS); if(OrderMagicNumber() == MagicNumber &&
OrderSymbol() == Symbol() && OrderComment() == OrderComment1) { // Sửa đổi thứ
tự đầu tiên } else if(OrderMagicNumber() == MagicNumber && OrderSymbol() ==
Symbol() && OrderComment() == OrderComment2) { // Sửa đổi thứ tự thứ hai } }
Chúng ta khai báo hai biến chuỗi để sử dụng làm nhận xét về thứ tự. Hàm OrderSend()
đặt hai đơn hàng, mỗi đơn hàng có một nhận xét đơn hàng khác nhau. Ví dụ về vòng
lặp sửa đổi đơn hàng sau đây sử dụng hàm OrderComment() làm điều kiện khi chọn
các đơn hàng cần sửa đổi. Bạn có thể sử dụng kiểm tra OrderComment() để đóng các
đơn đặt hàng độc lập với các đơn đặt hàng khác, sử dụng các cài đặt dừng cuối khác
hoặc bất cứ điều gì hệ thống giao dịch của bạn yêu cầu. 131 www.ZTCprep.com
CHUYÊN GIA CỐ VẤN LẬP TRÌNH Kiểm tra ký quỹ MetaTrader có các chức năng cho
phép bạn kiểm tra mức ký quỹ khả dụng hiện tại hoặc mức dừng trước khi đặt lệnh.
Mức dừng giao dịch là tỷ lệ phần trăm hoặc số tiền ký quỹ khả dụng mà dưới mức đó
bạn sẽ không thể đặt lệnh. Tuy nhiên, việc kiểm tra mức ký quỹ tự do hoặc mức dừng
lỗ theo cách thủ công trước khi đặt lệnh là không thực sự cần thiết vì sẽ xảy ra lỗi nếu
bạn cố đặt lệnh với mức ký quỹ quá ít. Một ý tưởng hữu ích hơn là xác định mức dừng
giao dịch của riêng bạn và tạm dừng giao dịch nếu vốn chủ sở hữu hiện tại xuống dưới
mức đó. Hãy bắt đầu bằng cách khai báo một biến bên ngoài có tên là MinimalEquity,
đây là số vốn sở hữu tối thiểu cần có trong tài khoản của chúng ta trước khi chúng ta
có thể đặt hàng. Chúng tôi sẽ so sánh Equity tối thiểu với vốn chủ sở hữu tài khoản
hiện tại của chúng tôi. Nếu vốn chủ sở hữu hiện tại nhỏ hơn vốn chủ sở hữu tối thiểu
của chúng tôi, đơn hàng sẽ không được đặt và một thông báo cảnh báo sẽ thông báo
cho người dùng về điều kiện. Giả sử chúng ta có số dư tài khoản là 10.000 USD. Nếu
chúng tôi mất hơn 20% số vốn đó, chúng tôi không muốn đặt lệnh. Đây là đoạn mã để
kiểm tra vốn chủ sở hữu tối thiểu: // Biến ngoài extern int MinimalEquity = 8000; // Đặt
lệnh if(AccountEquity() > MinimEquity) { // Đặt lệnh } else if(AccountEquity() <=
MinimalEquity) { Alert("Vốn sở hữu hiện tại nhỏ hơn vốn sở hữu tối thiểu! Chưa đặt
lệnh."); } Biến bên ngoài MinimalEquity được đặt ở đầu tệp. Phần còn lại của mã nằm
trước và sau chức năng đặt hàng. Nếu vốn chủ sở hữu hiện tại, như được biểu thị bởi
AccountEquity(), lớn hơn Vốn chủ sở hữu tối thiểu, đơn hàng sẽ được đặt. Nếu không,
đơn hàng sẽ không được đặt và thông báo cảnh báo sẽ được hiển thị. Kiểm tra mức
chênh lệch Bạn có thể muốn tránh đặt giao dịch trong những khoảng thời gian mà
mức chênh lệch đã mở rộng vượt xa mức bình thường. Chúng ta có thể đặt mức
chênh lệch tối đa và kiểm tra mức chênh lệch hiện tại trước khi giao dịch. Chúng ta sẽ
khai báo một biến bên ngoài có tên là MaximumSpread và sử dụng MarketInfo() để
kiểm tra mức chênh lệch hiện tại. 132 www.ZTCprep.com Mẹo và thủ thuật Mã này sẽ
rất giống với phần trước, nơi chúng tôi đã thêm kiểm tra ký quỹ tối thiểu. Chúng tôi sẽ
bao gồm mã từ phần trước để hiển thị cách các hoạt động kiểm tra khác nhau này
hoạt động cùng nhau: // Biến ngoài extern int MaximumSpread = 5; extern int Công
bằng tối thiểu = 8000; if(AccountEquity() > MinimalEquity &&
MarketInfo(Symbol(),MODE_SPREAD) < MaximumSpread) { // Đặt lệnh } else {
if(AccountEquity() <= MinimalEquity) Alert("Vốn sở hữu hiện tại nhỏ hơn vốn sở hữu tối
thiểu! Chưa đặt lệnh ."); if(MarketInfo(Symbol(),MODE_SPREAD) > MaximumSpread)
Alert("Chênh lệch hiện tại lớn hơn chênh lệch tối đa! Chưa đặt lệnh."); } Lưu ý rằng
chúng tôi thực hiện cả kiểm tra vốn chủ sở hữu tối thiểu và kiểm tra chênh lệch trước
khi đặt hàng. Nếu một trong các điều kiện sai, chúng ta đi tới khối else và kiểm tra
xem điều kiện nào khiến đơn hàng không được đặt. Chúng tôi sẽ hiển thị một hoặc
nhiều cảnh báo tùy thuộc vào điều kiện nào là đúng. Nhiều Lệnh Bạn có thể muốn đặt
nhiều lệnh cho mỗi vị thế với các mức dừng lỗ và chốt lời khác nhau, cũng như kích cỡ
lô. Có một số cách để thực hiện điều này. Một cách đơn giản là sử dụng câu lệnh
OrderSend() khác cho mỗi đơn hàng bạn muốn đặt. Điều này giả định rằng bạn dự
định đặt cùng một số lượng đơn hàng mỗi lần. Một cách khác là sử dụng vòng lặp for
để đặt hàng. Bằng cách này, bạn có thể điều chỉnh số lượng đơn đặt hàng cùng một
lúc. Bạn có thể tải trước điểm dừng lỗ của mình và tính giá lợi nhuận thành các mảng
rồi tăng dần qua các mảng trong vòng lặp for. Hãy bắt đầu bằng cách xác định các
biến bên ngoài cho ba mức dừng lỗ và chốt lời. Bất kỳ lệnh bổ sung nào trên ba sẽ
không có lệnh dừng lỗ hoặc chốt lời. Chúng tôi cũng sẽ thêm một biến bên ngoài để
điều chỉnh số lượng đơn đặt hàng. int bên ngoài StopLoss1 = 20; int bên ngoài
StopLoss2 = 40; int bên ngoài StopLoss3 = 60; 133 www.ZTCprep.com CHUYÊN GIA
CỐ VẤN LẬP TRÌNH extern int TakeProfit1 = 40; int bên ngoài TakeProfit2 = 80; int bên
ngoài TakeProfit3 = 120; extern int MaxOrders = 3; Tiếp theo, chúng ta sẽ khai báo các
mảng, tính toán mức dừng lỗ và chốt lời, đồng thời tải các mức giá đã tính vào mảng:
double BuyTakeProfit[3]; gấp đôi BuyStopLoss[3]; BuyTakeProfit[0] =
CalcBuyTakeProfit(Symbol(),TakeProfit1,Ask); BuyTakeProfit[1] =
CalcBuyTakeProfit(Symbol(),TakeProfit2,Ask); BuyTakeProfit[2] =
CalcBuyTakeProfit(Symbol(),TakeProfit3,Ask); BuyStopLoss[0] =
CalcBuyStopLoss(Symbol(),StopLoss1,Ask); BuyStopLoss[1] =
CalcBuyStopLoss(Symbol(),StopLoss2,Ask); BuyStopLoss[2] =
CalcBuyStopLoss(Symbol(),StopLoss3,Ask); Chúng tôi bắt đầu bằng cách khai báo
các mảng để giữ mức dừng lỗ và chốt lãi, BuyTakeProfit và BuyStopLoss. Khi khai báo
mảng phải ghi rõ số phần tử của mảng. Các chỉ mục mảng bắt đầu từ 0, do đó, bằng
cách khai báo kích thước kích thước mảng là 3, chỉ số bắt đầu của chúng tôi là 0 và
chỉ số lớn nhất của chúng tôi là 2. Tiếp theo, chúng tôi tính toán mức dừng lỗ và chốt
lãi bằng cách sử dụng các hàm mà chúng tôi đã xác định trong chương 4 –
CalcBuyStopLoss () và CalcBuyTakeProfit(). Chúng tôi chỉ định giá trị dừng lỗ hoặc
chốt lời được tính toán cho phần tử mảng thích hợp. Lưu ý rằng chỉ số mảng đầu tiên
là 0 và chỉ mục mảng thứ ba là 2. Đây là vòng lặp for để đặt hàng: for(int Count = 0;
Count <= MaxOrders - 1; Count++) { int OrdInt = Count + 1;
OrderSend(Symbol(),OP_BUY,LotSize,Ask,UseSlippage,BuyStopLoss[Count],
BuyTakeProfit[Count],"Mua đơn hàng "+OrdInt,MagicNumber,0,Green); } Biến Count bắt
đầu từ 0, tương ứng với phần tử mảng đầu tiên của chúng ta. Số lần lặp lại (tức là số
lượng lệnh đặt) được xác định bởi MaxOrders - 1. Đối với mỗi lần lặp của vòng lặp,
chúng ta tăng mảng dừng lỗ và chốt lãi lên một. 134 www.ZTCprep.com Mẹo và thủ
thuật Chúng tôi sử dụng biến OrdInt để tăng số lượng đơn hàng trong nhận xét đơn
hàng. Nhận xét đơn hàng đầu tiên sẽ là "Đơn hàng mua 1", nhận xét tiếp theo sẽ là
"Đơn hàng mua 2", v.v. Hàm OrderSend() đặt lệnh với giá trị dừng lỗ và chốt lãi thích
hợp, sử dụng biến Count để chọn phần tử mảng có liên quan. Đây chỉ là một cách để
xử lý nhiều đơn hàng, mặc dù nó có lẽ là cách hiệu quả nhất. Hạn chế chính của
phương pháp này là chúng ta chỉ có thể tính toán mức dừng lỗ và chốt lãi cho một số
lượng lệnh hạn chế. Ngoài ra, chúng ta có thể chia tỷ lệ giá trị chốt lãi và dừng lỗ theo
một lượng nhất định và đặt số lượng lệnh có khả năng không giới hạn: extern int
StopLossStart = 20; int bên ngoài StopLossIncr = 20; extern int TakeProfitStart = 40;
extern int TakeProfitIncr = 40; extern int MaxOrders = 5; Trong ví dụ trên, mức dừng lỗ
cho lệnh đầu tiên của chúng ta sẽ là 20 pip. Chúng tôi sẽ tăng mức dừng lỗ thêm 20
pip cho mỗi lệnh bổ sung. Tương tự đối với lệnh chốt lời, ngoại trừ việc chúng ta sẽ bắt
đầu ở mức 40 và tăng dần lên 40. Thay vì sử dụng mảng, chúng ta sẽ tính toán mức
dừng lỗ và chốt lãi trong vòng lặp for: for(int Count = 0; Count <= MaxOrders - 1;
Đếm++) { int OrdInt = Đếm + 1; int UseStopLoss = StopLossStart + (StopLossIncr *
Đếm); int UseTakeProfit = TakeProfitStart + (TakeProfitIncr * Đếm); gấp đôi
BuyStopLoss = CalcBuyStopLoss(Symbol(),UseStopLoss,Ask); gấp đôi BuyTakeProfit
= CalcBuyTakeProfit(Symbol(),UseTakeProfit,Ask);
OrderSend(Symbol(),OP_BUY,LotSize,Ask,UseSlippage,BuyStopLoss,
BuyTakeProfit,"Lệnh mua "+OrdInt,MagicNumber,0,Green); } Chúng tôi xác định mức
chốt lời và dừng lỗ tính bằng pip bằng cách nhân biến StopLossIncr hoặc
TakeProfitIncr với Số lượng và thêm giá trị đó vào giá trị StopLossStart hoặc
TakeProfitStart. Đối với lệnh đầu tiên, mức dừng lỗ hoặc chốt lời sẽ bằng
StopLossStart hoặc TakeProfitStart. Tiếp theo, chúng ta tính toán mức dừng lỗ và chốt
lời cho lệnh bằng cách sử dụng các hàm của chúng ta từ chương 4. Cuối cùng, chúng
ta đặt lệnh bằng OrderSend(). Vòng lặp sẽ tiếp tục cho đến khi số lượng đơn đặt hàng
được đặt. Phương pháp này cho phép chúng tôi chỉ định bao nhiêu đơn hàng tùy thích
bằng cách sử dụng biến MaxOrders, đảm bảo rằng mọi đơn hàng chúng tôi đặt sẽ có
mức dừng lỗ và chốt lời. Biến toàn cục Trong cuốn sách này, chúng tôi đã đề cập đến
các biến có phạm vi toàn cầu là "biến toàn cục". MetaTrader có một bộ chức năng để
thiết lập các biến ở cấp độ cuối, có nghĩa là các biến này có sẵn cho mọi cố vấn
chuyên gia hiện đang chạy, giả sử rằng chúng ta biết tên của biến để bắt đầu. Tài liệu
MQL gọi chúng là "biến toàn cục", mặc dù tên thích hợp hơn có thể là "biến đầu cuối".
Chúng tôi sử dụng các hàm biến toàn cục trong Tham chiếu MQL trong Biến toàn cục
để làm việc với các loại biến này. Có thể xem danh sách các biến toàn cục hiện tại
trong thiết bị đầu cuối bằng cách chọn Biến toàn cục từ menu Công cụ hoặc bằng
cách nhấn F3 trên bàn phím. Một cách để sử dụng các biến này là lưu trữ các biến tĩnh
hoặc có phạm vi toàn cầu nhất định vào thiết bị đầu cuối, để nếu cố vấn chuyên gia bị
tắt, chúng tôi có thể tiếp tục từ nơi chúng tôi đã dừng lại. Không phải tất cả các cố vấn
chuyên gia đều yêu cầu điều này, nhưng các cố vấn chuyên gia phức tạp hơn sẽ duy trì
một trạng thái nhất định mà nếu bị gián đoạn sẽ làm ảnh hưởng đến hoạt động của cố
vấn chuyên gia. Cách tốt nhất để ngăn chặn điều này là tránh tạo ra các chuyên gia cố
vấn đòi hỏi mức độ phức tạp như vậy. Nhưng nếu không thể tránh được thì việc sử
dụng các hàm biến toàn cục để lưu trữ trạng thái hiện tại vào thiết bị đầu cuối có thể
hữu ích trong trường hợp vô tình tắt máy. Lưu ý rằng phương pháp này không hoàn
hảo nhưng có thể là phương pháp tốt nhất để đạt được điều này. Để khai báo biến
toàn cục (đầu cuối), hãy sử dụng hàm GlobalVariableSet(). Đối số đầu tiên là một
chuỗi biểu thị tên của biến toàn cục và đối số thứ hai là giá trị kiểu double để gán cho
nó. GlobalVariableSet(GlobalVariableName,DoubleValue); Để giữ tên biến của bạn là
duy nhất, bạn có thể muốn tạo tiền tố biến toàn cục. Khai báo một biến có phạm vi
toàn cầu trong Expert Advisor của bạn và đặt giá trị trong hàm init(), sử dụng ký hiệu,
dấu chấm, tên Expert Advisor và số ma thuật hiện tại để tạo tiền tố biến duy nhất. 136
www.ZTCprep.com Mẹo và thủ thuật // Chuỗi biến toàn cục GlobalVariablePrefix; int
init() { GlobalVariablePrefix =
Symbol()+Period()+"_"+"ProfitBuster"+"_"+MagicNumber+"_"; } Chúng tôi sử dụng ký
hiệu và dấu chấm hiện tại, cùng với mã định danh cho EA và biến bên ngoài
MagicNumber. Bây giờ, khi đặt biến toàn cục bằng GlobalVariableSet(), chúng ta sử
dụng tiền tố đã xác định ở trên, cùng với tên biến thực tế:
GlobalVariableSet(GlobalVariablePrefix+Counter,Counter); Vì vậy, nếu chúng ta đang
giao dịch trên EURUSD trên khung thời gian M15 với EA có tên là "ProfitBuster", sử
dụng 11 làm con số kỳ diệu và Counter làm tên biến, tên của biến toàn cầu sẽ là
EURUSD15_ProfitBuster_11_Counter. Bạn có thể sử dụng bất kỳ quy ước nào bạn
muốn để đặt tên cho các biến toàn cục của mình, nhưng bạn nên bao gồm các thông
tin trên. Để truy xuất giá trị của một biến toàn cục, hãy sử dụng hàm
GlobalVariableGet() với tên biến làm đối số: Counter =
GlobalVariableGet(GlobalVariablePrefix+Counter); Để xóa một biến toàn cục, hãy sử
dụng hàm GlobalVariableDel() với tên biến làm đối số. Để xóa tất cả các biến toàn cục
do EA của bạn đặt, hãy sử dụng hàm GlobalVariableDeleteAll() với tiền tố của bạn làm
đối số. GlobalVariableDel(GlobalVariablePrefix+Counter);
GlobalVariableDeleteAll(GlobalVariablePrefix); Để biết thêm thông tin về các hàm biến
toàn cục, hãy xem chủ đề Biến toàn cục trong Tài liệu tham khảo MQL. Kiểm tra lợi
nhuận của đơn hàng Đôi khi, việc kiểm tra lợi nhuận hiện tại trên một đơn hàng hoặc
kiểm tra tổng lợi nhuận trên một đơn hàng đã đóng có thể hữu ích. Có hai cách để
kiểm tra lợi nhuận. Để nhận được lợi nhuận bằng loại tiền gửi, hãy sử dụng hàm
OrderProfit(). Trước tiên bạn phải chọn thứ tự bằng OrderSelect(). 137
www.ZTCprep.com CHUYÊN GIA CỐ VẤN LẬP TRÌNH
OrderSelect(Ticket,SELECT_BY_TICKET); nhân đôi GetProfit = OrderProfit(Vé); Kết quả
của hàm OrderProfit() phải giống với tổng lãi hoặc lỗ được liệt kê trong lịch sử đơn
hàng cho đơn hàng đã chọn. Để tính lãi hoặc lỗ tính bằng pip, bạn sẽ cần tính chênh
lệch giữa giá mở lệnh và giá đóng lệnh. Bạn cũng sẽ cần sử dụng hàm OrderSelect()
để truy xuất giá mở và đóng. OrderSelect(Vé,SELECT_BY_TICKET); if(OrderType() ==
OP_BUY) nhân đôi GetProfit = OrderClosePrice() - OrderOpenPrice(); khác
nếu(OrderType() == OP_SELL) GetProfit = OrderOpenPrice() - OrderClosePrice();
GetProfit /= PipPoint(Symbol()); Đối với các lệnh mua, chúng ta tính lợi nhuận bằng
cách lấy giá đóng cửa trừ đi giá mở cửa. Đối với lệnh bán chúng ta làm ngược lại. Sau
khi tính toán chênh lệch, chúng ta có thể chuyển lãi hoặc lỗ thành số nguyên bằng
cách chia cho điểm, sử dụng hàm PipPoint(). Ví dụ: nếu giá mở lệnh mua của chúng
tôi là 1,4650 và giá đóng cửa của chúng tôi là 1,4700, thì chênh lệch giữa
OrderClosePrice() và OrderOpenPrice() là 0,0050. Khi chúng tôi chia số đó cho hàm
PipPoint(), kết quả là 50. Vì vậy, với đơn hàng này, chúng tôi kiếm được 50 pip lợi
nhuận. Thay vào đó, nếu giá đóng lệnh là 1,4600 thì chúng ta sẽ lỗ -50 pip. Martingale
Martingale là một hệ thống cá cược, thường được sử dụng trong roulette và blackjack,
trong đó mức đặt cược được tăng gấp đôi sau mỗi lần thua liên tiếp. Về lý thuyết, một
lần đặt cược thắng sẽ đưa số dư trở lại mức hòa vốn. Nhược điểm của Martingale là
bạn cần rất nhiều vốn để chịu được các khoản rút vốn. Ví dụ: nếu kích thước lô ban
đầu của bạn là 0,1 lô, sau 4 lần thua liên tiếp, kích thước lô của bạn sẽ là 1,6 lô - gấp
16 lần kích thước lô ban đầu của bạn. Sau 7 lần thua liên tiếp, kích thước lô của bạn sẽ
là 12,8 lô – gấp 128 lần kích thước lô ban đầu của bạn! Một chuỗi thua lỗ kéo dài sẽ
xóa sạch tài khoản của bạn trước khi bạn có thể đưa tài khoản của mình trở lại mức
hòa vốn. Tuy nhiên, bạn có thể muốn kết hợp một hệ thống tăng kích thước lô khi
thắng hoặc thua liên tiếp và bạn có thể làm như vậy mà không cần xóa sạch tài khoản
của mình. Phương pháp đơn giản nhất là đặt giới hạn về số lần để tăng kích thước lô.
Một hệ thống giao dịch hợp lý không được có nhiều hơn 3 138 www.ZTCprep.com
Mẹo và thủ thuật hoặc 4 lần thua liên tiếp tối đa. Bạn có thể xác định điều này bằng
cách kiểm tra số lần thua lỗ liên tiếp tối đa trong tab Báo cáo trong cửa sổ Trình kiểm
tra chiến lược. Một phương pháp khác là tăng kích thước lô của bạn bằng hệ số nhân
nhỏ hơn. Chiến lược Martingale cổ điển nhân đôi kích thước lô sau mỗi lần thua liên
tiếp. Bạn có thể muốn sử dụng hệ số nhân nhỏ hơn 2. Ngoài ra còn có chiến lược
chống Martingale, trong đó bạn tăng kích thước lô sau mỗi lần thắng liên tiếp. Hãy
xem xét một quy trình trong đó chúng ta tính toán số lần thắng hoặc thua liên tiếp và
tăng quy mô lô tương ứng. Chiến lược Martingale hoạt động tốt nhất khi bạn đặt một
lệnh mỗi lần, vì vậy chúng ta sẽ giả định rằng mọi vị thế đều bao gồm một giao dịch
duy nhất. Người dùng sẽ có thể lựa chọn giữa chiến lược Martingale (thua) hoặc
chống Martingale (thắng). Một cài đặt để giới hạn số lần tăng lô liên tiếp tối đa sẽ
được bao gồm và hệ số nhân lô sẽ được điều chỉnh. Đầu tiên chúng ta hãy tính số trận
thắng hoặc thua liên tiếp. Chúng ta sẽ cần lặp ngược lại nhóm lịch sử đơn hàng, bắt
đầu từ đơn hàng đã đóng gần đây nhất. Chúng tôi sẽ tăng một bộ đếm cho mỗi lần
thắng hoặc thua. Miễn là mô hình thắng hoặc thua liên tiếp được duy trì, chúng ta sẽ
tiếp tục lặp lại. Ngay khi mô hình bị phá vỡ (thắng được xác định sau một hoặc nhiều
lần thua hoặc ngược lại), vòng lặp sẽ thoát ra. int WinCount; int LossCount; for(int
Count = OrderHistoryTotal()-1; ; Count--) {
OrderSelect(Count,SELECT_BY_POS,MODE_HISTORY); if(OrderSymbol() == Symbol()
&& OrderMagicNumber() == MagicNumber) { if(OrderProfit() > 0 && LossCount == 0)
WinCount++; khác if(OrderProfit() < 0 && WinCount == 0) LossCount++; nếu không thì
phá vỡ; } } Chúng ta bắt đầu bằng việc khai báo các biến cho bộ đếm thắng và thua.
Trong toán tử for, hãy lưu ý rằng chúng ta sử dụng OrderHistoryTotal() để thiết lập vị trí
bắt đầu ban đầu. OrderHistoryTotal() trả về số lượng đơn hàng trong nhóm lịch sử.
Chúng tôi trừ 1 để xác định vị trí chỉ mục cho đơn hàng gần đây nhất, được lưu trong
biến Count. Lưu ý rằng chúng ta đã bỏ qua biểu thức thứ hai trong vòng lặp for – biểu
thức xác định điều kiện để dừng vòng lặp. Dấu chấm phẩy phải được giữ nguyên cho
mọi biểu thức bị bỏ qua. Chúng ta sẽ giảm biến Count trên mỗi lần lặp của vòng lặp.
139 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Chúng tôi sử dụng
MODE_HISTORY làm đối số thứ ba trong hàm OrderSelect() để chỉ ra rằng chúng tôi
đang lặp qua nhóm lịch sử lệnh đã đóng. Theo mặc định, OrderSelect() sử dụng nhóm
lệnh mở, vì vậy chúng ta phải chỉ định MODE_HISTORY khi kiểm tra nhóm lệnh đã
đóng. Chúng tôi kiểm tra để đảm bảo rằng thứ tự hiện được chọn khớp với biểu tượng
biểu đồ và con số kỳ diệu của chúng tôi. Sau đó, chúng tôi kiểm tra lợi nhuận của đơn
hàng bằng hàm OrderProfit(). Nếu giá trị trả về biểu thị lợi nhuận (tức là lớn hơn 0), thì
chúng ta tăng biến WinCount. Nếu đó là thua lỗ, chúng tôi sẽ tăng LossCount. Vì
chúng ta đang tìm kiếm thắng hoặc thua liên tiếp nên chúng ta cần kết thúc vòng lặp
khi tìm thấy điều kiện xen kẽ. Để làm điều này, chúng tôi kiểm tra biến WinCount hoặc
LossCount khi kiểm tra lợi nhuận của đơn hàng. Ví dụ: nếu chúng ta thua 2 trận liên
tiếp – nghĩa là LossCount = 2 – và lệnh tiếp theo của chúng ta là thắng, thì cả hai câu
lệnh if của chúng ta sẽ sai và quyền điều khiển sẽ được chuyển đến toán tử break,
toán tử này sẽ kết thúc vòng lặp. Ưu điểm của phương pháp này là nó mạnh mẽ và sẽ
không bị lỗi nếu chuyên gia cố vấn vô tình bị tắt. EA sẽ tiếp tục ngay từ nơi nó đã dừng
lại. Tất nhiên, điều này có nghĩa là khi bạn khởi động EA lần đầu tiên, nó sẽ sử dụng
bất kỳ chuỗi thắng/thua nào trước đó khi xác định kích thước lô. Nhưng như bạn có
thể thấy, ưu điểm nhiều hơn nhược điểm. Biến WinCount hoặc LossCount sẽ chứa số
trận thắng hoặc thua liên tiếp. Nếu chúng tôi muốn thực hiện chiến lược Martingale,
chúng tôi sử dụng LossCount để xác định yếu tố cần tăng kích thước lô. Nếu chúng tôi
đang thực hiện chống Martingale, thay vào đó chúng tôi sẽ sử dụng WinCount. Chúng
tôi sẽ sử dụng biến số nguyên bên ngoài có tên MartingaleType để xác định điều này.
Nếu MartingaleType được đặt thành 0, chúng tôi sẽ sử dụng chiến lược Martingale.
Nếu nó được đặt thành 1, chúng tôi sẽ sử dụng chiến lược chống Martingale. Chúng
tôi cũng sẽ khai báo các biến bên ngoài cho hệ số nhân (LotMultiplier), số lần tối đa
để tăng kích thước lô (MaxMartingale) và kích thước lô bắt đầu của chúng tôi
(BaseLotSize). // Biến ngoài extern int MartingaleType = 0; // 0: Martingale, 1: Anti-
Martingale extern int LotMultiplier = 2; extern int MaxMartingale = 4; bên ngoài gấp đôi
BaseLotSize = 0,1; // Tính toán kích thước lô if(MartingaleType == 0) int
ConsecutiveCount = LossCount; khác nếu(MartingaleType = 1) ConsecutiveCount =
WinCount; if(ConsecutiveCount > MaxMartingale) ConsecutiveCount =
MaxMartingale; gấp đôi LotSize = BaseLotSize *
MathPow(LotMultiplier,ConsecutiveCount); 140 www.ZTCprep.com Mẹo và thủ thuật
Chúng tôi đặt giá trị của ConsecutiveCount thành WinCount hoặc LossCount, tùy
thuộc vào cài đặt MartingaleType. Chúng tôi sẽ so sánh điều đó với cài đặt
MaxMartingale của chúng tôi. Nếu số lượng đơn hàng liên tiếp của chúng tôi lớn hơn
MaxMartingale, chúng tôi sẽ thay đổi kích thước của nó để bằng MaxMartingale. (Bạn
cũng có thể thay đổi kích thước lô thành kích thước lô mặc định nếu muốn.) Kích
thước lô sẽ giữ nguyên ở kích thước này cho đến khi thắng hoặc thua phá vỡ chuỗi
lệnh liên tiếp của chúng tôi. Kích thước lô được xác định bằng cách nhân BaseLotSize
của chúng tôi với LotMultiplier, được tăng theo cấp số nhân bởi ConsecutiveCount.
Hàm MathPow() nâng một số lên lũy thừa được chỉ định. Đối số đầu tiên là cơ số và
đối số thứ hai là số mũ. Ví dụ: nếu kích thước lô bắt đầu của chúng tôi là 0,1, hệ số
nhân lô là 2 và chúng tôi có bốn đơn hàng liên tiếp, phương trình 4 là 0,1 * 2 = 1,6.
Bằng cách điều chỉnh LotMultiplier và sử dụng cả chiến lược Martingale và chiến lược
chống Martingale, điều này sẽ cung cấp cho bạn đủ tùy chọn để thử nghiệm việc sử
dụng kích thước lô theo cấp số nhân. Bạn có thể dễ dàng sửa đổi mã ở trên để sử
dụng các biến thể khác. Ví dụ: bạn có thể chia tỷ lệ kích thước lô theo chiều ngược lại,
từ lớn nhất đến nhỏ nhất. Hoặc bạn có thể sử dụng bộ đếm bên ngoài thay cho
ConsecutiveCount. Gỡ lỗi Expert Advisor của bạn Không giống như hầu hết các IDE lập
trình, MetaEditor không hỗ trợ các điểm dừng hoặc bất kỳ loại kỹ thuật gỡ lỗi hiện đại
nào khác. Bạn sẽ cần sử dụng các câu lệnh và nhật ký Print() để gỡ lỗi cho chuyên gia
cố vấn của mình. Bạn đã được làm quen với hàm Print(). Tóm lại, mọi đối số chuỗi
được truyền cho hàm sẽ được in ra nhật ký. Bằng cách in nội dung của các biến và
hàm vào nhật ký, bạn có thể kiểm tra đầu ra của mã và sửa mọi lỗi. Bạn sẽ muốn sử
dụng Trình kiểm tra chiến lược để chạy mô phỏng giao dịch và kiểm tra kết quả nhật
ký. Nhật ký của Trình kiểm tra chiến lược được hiển thị trong tab Nhật ký trong cửa sổ
Trình kiểm tra chiến lược. Có giới hạn về lượng thông tin được liệt kê trong tab Nhật ký,
vì vậy bạn có thể muốn xem nhật ký thực tế. Nhật ký của Trình kiểm tra chiến lược
được lưu trữ trong thư mục \tester\logs. Nhấp chuột phải vào bất kỳ đâu trong cửa sổ
Nhật ký và chọn Mở từ menu bật lên. Cửa sổ Windows Explorer sẽ mở ra, hiển thị nội
dung của thư mục nhật ký. Tên tệp có định dạng yyyymmdd.log, trong đó yyyy là năm
có bốn chữ số, mm là tháng có hai chữ số và dd là ngày có hai chữ số. Bạn có thể xem
nhật ký trong Notepad hoặc bất kỳ trình soạn thảo văn bản nào. Hãy minh họa một ví
dụ về cách bạn có thể sử dụng nhật ký để xác định vấn đề lập trình. Mã bên dưới có lỗi
và nó không hoạt động như chúng tôi mong đợi. Để có thể chẩn đoán được vấn đề,
141 www.ZTCprep.com CHƯƠNG TRÌNH TƯ VẤN CHUYÊN NGHIỆP chúng ta cần kiểm
tra đầu vào hoặc đầu ra của hàm. Hãy tạo một câu lệnh Print() và in nội dung của tất
cả các biến có liên quan vào nhật ký. Chúng tôi sẽ chạy EA trong Trình kiểm tra chiến
lược, chỉ sử dụng Giá mở làm mô hình thử nghiệm. Hãy đảm bảo rằng bạn đang thử
nghiệm EA trong một khoảng thời gian đủ dài để EA có thể đặt đủ giao dịch cho chúng
tôi phân tích. Nếu bạn cần kiểm tra giá trên biểu đồ, hãy nhấn nút Mở biểu đồ để mở
biểu đồ hiển thị các giao dịch được mô phỏng. Tiếp theo, chúng ta sẽ chuyển đến tab
Nhật ký và kiểm tra thông tin chúng ta cần. Nếu chúng ta cần xem toàn bộ nhật ký
hoặc nếu có giao dịch không hiển thị trong tab Nhật ký, chúng ta có thể nhấp chuột
phải và chọn Mở từ menu bật lên và mở tệp nhật ký trực tiếp. Mã này đang gây ra lỗi
130: "điểm dừng không hợp lệ" mỗi khi chúng tôi đặt lệnh mua. Chúng tôi biết rằng lỗi
130 có nghĩa là lệnh dừng lỗ hoặc chốt lãi không chính xác. Bạn có thể xác định được
lỗi không? if(Close[0] > MA && BuyTicket == 0) { double Openprice = Hỏi; gấp đôi
BuyStopLoss = OpenPrice + (StopLoss * UsePoint); gấp đôi BuyTakeProfit = OpenPrice
+ (TakeProfit * UsePoint); BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,Openprice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Mua đơn hàng",MagicNumber,0,Green); BánTicket = 0; }
Chúng ta sẽ sử dụng hàm Print() để xác minh các tham số đang được truyền cho hàm
OrderSend(). Chúng ta sẽ tập trung vào giá mở lệnh, mức dừng lỗ và chốt lời.
Print("Giá:"+OpenPrice+" Dừng:"+BuyStopLoss+" Lợi nhuận:"+BuyTakeProfit); Đây là
kết quả khi chúng tôi chạy EA trong trình thử nghiệm chiến lược. Giả sử mức dừng lỗ
và chốt lời là 50 pips: 11:52:12 2009.11.02 02:00 Ví dụ EURUSD,H1: Lỗi gửi lệnh 130
11:52:12 2009.11.02 02:00 Ví dụ EURUSD,H1: Giá:1.47340000 Dừng:1.47840000 Lợi
nhuận:1.47840000 Chúng tôi biết rằng mức dừng lỗ phải thấp hơn giá mở cửa cho
một lệnh mua. Ở đây, nó cao hơn giá. Trên thực tế, nó có cùng mức giá với mức chốt
lời. Xem nhanh mã của chúng tôi và chúng tôi nhận ra rằng chúng tôi đã vô tình chèn
dấu cộng vào phương trình dừng lỗ mua. Đây là mã chính xác: 142 www.ZTCprep.com
Mẹo và thủ thuật nhân đôi BuyStopLoss = OpenPrice - (StopLoss * UsePoint); Nếu bạn
nhận được thông báo lỗi khi cố gắng đặt, đóng hoặc sửa đổi đơn hàng, hãy tập trung
nỗ lực vào vấn đề được chỉ ra bởi thông báo lỗi. Dưới đây là một số thông báo lỗi phổ
biến nhất do lỗi lập trình gây ra: • Lỗi 129: Giá không hợp lệ – Giá mở cửa không hợp
lệ. Đối với các lệnh thị trường, hãy đảm bảo giá Mua hoặc Bán hiện tại đang được
thông qua, tùy theo loại lệnh. Đối với các lệnh đang chờ xử lý, hãy đảm bảo giá cao
hơn hoặc thấp hơn giá hiện tại, theo yêu cầu của loại lệnh. Đồng thời kiểm tra xem giá
lệnh chờ xử lý có quá gần với giá hiện tại hay không (tức là nằm trong mức dừng). • Lỗi
130: Điểm dừng không hợp lệ – Giá dừng lỗ hoặc chốt lãi không chính xác. Kiểm tra
xem giá dừng lỗ và chốt lời được đặt ở trên hay dưới giá hiện tại, tùy thuộc vào loại
lệnh là mua hay bán. Đồng thời kiểm tra xem giá dừng lỗ hoặc chốt lãi không quá gần
với giá hiện tại (tức là nằm trong mức dừng). • Lỗi 131: Khối lượng giao dịch không
hợp lệ – Kích thước lô không chính xác. Đảm bảo rằng kích thước lô không vượt quá
mức tối thiểu hoặc tối đa của nhà môi giới và kích thước lô được chuẩn hóa thành giá
trị bước chính xác (0,1 hoặc 0,01 trên hầu hết các nhà môi giới). Bạn có thể tìm thấy
mô tả của tất cả các thông báo lỗi trong Tham chiếu MQL trong Hằng số tiêu chuẩn –
Mã lỗi. Nếu bạn cần hỗ trợ thêm về lỗi mà bạn đang gặp phải, hãy kiểm tra các diễn
đàn tại MQL4.com. Khắc phục sự cố các lỗi giao dịch không liên tục Trong khi hầu hết
các lỗi nghiêm trọng có thể được tìm thấy đơn giản bằng cách kiểm tra lại, những lỗi
khác sẽ chỉ xảy ra trong quá trình giao dịch theo thời gian thực. Lỗi logic có thể dẫn
đến giao dịch không được đặt chính xác và bạn có thể phải mất một chút nỗ lực để
xác định những lỗi này. Nếu có giao dịch được đặt không chính xác trong quá trình
giao dịch demo hoặc giao dịch trực tiếp, chúng tôi cần nhiều thông tin cần thiết để
khắc phục sự cố. Chúng tôi sẽ thêm một tính năng tùy chọn để ghi nhật ký thông tin
trạng thái và giao dịch theo thời gian thực để chúng tôi có bản ghi về nó khi khắc phục
sự cố giao dịch. Chúng tôi sẽ sử dụng câu lệnh Print() như trước đây nhưng chúng tôi
sẽ ghi lại các giá trị chỉ báo, giá cả – bất kỳ thông tin nào hữu ích trong việc gỡ lỗi.
Chúng tôi cũng sẽ thêm một biến bên ngoài để bật và tắt đăng nhập. // Biến ngoài
extern bool Debug = true; 143 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN
NGHIỆP // Đặt ở gần cuối hàm start() if(Debug == true)
Print(StringConcatenate("Bid:",Bid," Ask:",Ask," MA:" ,MA, " MuaTicket:",BuyTicket,"
SellTicket:",SellTicket)); Đoạn mã trên sẽ ghi lại thông tin về giá và chỉ báo cũng như
nội dung của các biến BuyTicket và SellTicket. Nếu có bất kỳ câu hỏi nào về cách mở
giao dịch hoặc tại sao giao dịch không được mở, nhật ký tại thời điểm cụ thể đó sẽ
hiển thị trạng thái của tất cả các điều kiện giao dịch có liên quan. Bạn có thể bật và tắt
đăng nhập bằng biến ngoài Debug. Câu lệnh debug Print() phải được đặt ở gần cuối
hàm start(), sau tất cả các hàm giao dịch. Nếu bạn đang sử dụng bộ hẹn giờ và/hoặc
tính năng thực thi khi mở thanh, hãy đặt câu lệnh debug Print() bên trong khối bộ hẹn
giờ để nó chỉ chạy khi cần thiết. Nếu không, dòng gỡ lỗi sẽ in ra nhật ký sau mỗi tích
tắc, điều này có thể tạo ra một tệp nhật ký lớn. Sửa lỗi biên dịch Khi bạn biên dịch
Expert Advisor, trình biên dịch sẽ kiểm tra cú pháp đúng và đảm bảo rằng tất cả các
hàm và biến tùy chỉnh đã được khai báo đúng. Nếu bạn bỏ sót nội dung nào đó, trình
biên dịch sẽ dừng và mọi lỗi biên dịch sẽ xuất hiện trong tab Lỗi trong cửa sổ Hộp
công cụ. Khi gặp phải một danh sách dài các lỗi biên dịch, hãy luôn bắt đầu từ lỗi đầu
tiên. Bấm đúp vào lỗi trong danh sách và trình soạn thảo sẽ chuyển trực tiếp đến dòng
có lỗi. Sửa lỗi và biên dịch lại. Đôi khi một lỗi cú pháp đơn giản sẽ dẫn đến một số lỗi
không liên quan, mặc dù chỉ có lỗi đầu tiên là hợp lệ. Đây là danh sách các lỗi biên
dịch thường gặp và cách khắc phục: • Biến không được xác định – Bạn quên khai báo
một biến có kiểu dữ liệu. Nếu đó là biến toàn cục hoặc biến ngoài, hãy khai báo nó ở
đầu tệp. Nếu đó là biến cục bộ, hãy tìm lần xuất hiện đầu tiên và đặt phần khai báo
kiểu dữ liệu trước biến đó. Nếu không, hãy kiểm tra chính tả hoặc kiểu chữ
(hoa/thường) của tên biến. • Biến đã được xác định – Bạn đã khai báo cùng một biến
hai lần. Loại bỏ phần khai báo kiểu dữ liệu khỏi tất cả các khai báo biến trùng lặp. •
Hàm không được xác định – Nếu hàm được đề cập nằm trong tệp bao gồm hoặc thư
viện, hãy đảm bảo rằng lệnh #include hoặc #import được đặt ở đầu tệp và đúng. Nếu
không, hãy kiểm tra chính tả hoặc kiểu chữ của tên hàm và đảm bảo rằng nó tồn tại
trong tệp hiện tại hoặc trong tệp bao gồm hoặc thư viện có liên quan. 144
www.ZTCprep.com Mẹo và thủ thuật • Phép gán bất hợp pháp được sử dụng – Điều
này thường liên quan đến dấu bằng (=). Hãy nhớ rằng một dấu bằng duy nhất dành
cho phép gán biến và hai dấu bằng (==) là toán tử so sánh. Sửa toán tử gán thành toán
tử so sánh thích hợp. • Phép gán dự kiến – Điều này thường liên quan đến toán tử so
sánh "bằng" (==). Bạn đã sử dụng hai dấu bằng thay vì một trong phép gán biến. Sửa
toán tử thành một dấu bằng duy nhất. • Dấu ngoặc đơn bên phải không cân bằng –
Điều này thường xảy ra trong câu lệnh if khi sử dụng dấu ngoặc đơn lồng nhau. Đi đến
dòng được chỉ ra bởi lỗi đầu tiên và chèn dấu ngoặc đơn bên trái vào vị trí thích hợp. •
Dấu ngoặc trái không cân bằng – Đây là một dấu ngoặc đơn khó. Lỗi thường xảy ra ở
cuối dòng chương trình. Về cơ bản bạn đã quên dấu ngoặc đơn ở đâu đó. Kiểm tra kỹ
mã bạn đã chỉnh sửa gần đây và tìm dấu ngoặc đơn bên phải bị thiếu. Bạn có thể phải
nhận xét các dòng mã để xác định vấn đề. • Đếm tham số sai – Bạn có quá ít hoặc
quá nhiều đối số trong một hàm. Kiểm tra kỹ cú pháp hàm trong Tham chiếu MQL và
sửa các đối số. • Cần có dấu chấm phẩy – Có thể bạn đã quên đặt dấu chấm phẩy ở
cuối dòng. Đặt dấu chấm phẩy ở cuối dòng trước. Lưu ý rằng thiếu dấu chấm phẩy
cũng có thể gây ra bất kỳ lỗi nào ở trên, vì vậy hãy nhớ đặt các dấu chấm phẩy đó! 145
www.ZTCprep.com CHƯƠNG TRÌNH CỐ VẤN CHUYÊN GIA Chương 9 Các chỉ báo và
tập lệnh tùy chỉnh Không có cuốn sách nào về MQL sẽ hoàn chỉnh nếu không đề cập
đến các chỉ báo và tập lệnh tùy chỉnh. Các chỉ báo tích hợp trong MetaTrader khá hạn
chế, nhưng may mắn thay MQL cho phép các lập trình viên tạo các chỉ báo của riêng
họ. Nếu bạn đang tìm kiếm một chỉ báo phổ biến không có trong MT4 thì rất có thể ai
đó đã tạo một chỉ báo đó. Chương này sẽ là tổng quan cơ bản về việc tạo chỉ báo tùy
chỉnh. Hầu hết các chỉ báo đều sử dụng các công thức toán học phức tạp và đó là lĩnh
vực của các lập trình viên giàu kinh nghiệm hơn. Tuy nhiên, một chỉ báo không cần
phải phức tạp. Chúng ta sẽ tạo một chỉ báo tùy chỉnh trong chương này chỉ sử dụng
một vài dòng mã. Bộ đệm Bộ đệm là mảng lưu trữ các giá trị chỉ báo và phép tính. Một
chỉ báo tùy chỉnh có thể có tối đa 8 bộ đệm. Bộ đệm sử dụng các chỉ mục, giống như
mảng và có phạm vi từ 0 đến 7. Khi bạn gọi một chỉ báo tùy chỉnh trong chuyên gia tư
vấn bằng cách sử dụng hàm iCustom(), tham số tiếp theo trong hàm là bộ đệm chỉ
báo. Để tìm vùng đệm thích hợp cho dòng chỉ báo, bạn thường kiểm tra mã nguồn, nếu
có. Nếu mã nguồn được định dạng rõ ràng với các tên biến mô tả, bạn sẽ có thể xác
định bộ đệm thích hợp khá dễ dàng. Chúng ta sẽ đề cập đến cách đặt tên thích hợp
cho bộ đệm chỉ báo trong chương này. Tạo chỉ báo tùy chỉnh Hãy xây dựng một chỉ
báo tùy chỉnh bằng cách sử dụng hai chỉ báo MetaTrader tích hợp để tính toán các
đường của chúng tôi. Chúng ta sẽ xây dựng một chỉ báo Dải Bollinger đã được sửa
đổi. Dải Bollinger bao gồm 3 đường – đường trung tâm là đường trung bình động đơn
giản, cùng với đường trên và đường dưới có giá trị được xác định bằng độ lệch chuẩn.
Chúng ta có thể tạo chỉ báo Dải bollinger của riêng mình bằng cách sử dụng các chỉ
báo Trung bình trượt và Độ lệch chuẩn. Chúng tôi muốn tạo một chỉ báo sử dụng
đường trung bình động hàm mũ để tính toán các đường, trái ngược với đường trung
bình động đơn giản. 146 www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Chúng
tôi bắt đầu bằng cách sử dụng trình hướng dẫn để tạo tệp chỉ báo của mình. Chọn Mới
từ menu Tệp hoặc thanh công cụ để mở trình hướng dẫn và tạo chỉ báo tùy chỉnh. Điền
tên chỉ báo và thêm thông số nếu muốn. Trên trang cuối cùng, chúng tôi đã thêm ba
dòng chỉ báo cùng màu. Đây là kết quả của thuật sĩ. Hiện tại chúng ta đã bỏ qua hàm
start(): //+--------------------------------- --------------------------------+ //| EMA Bollinger.mq4 | //|
Andrew trẻ | //| http://www.easyexpertforex.com | //+-----------------------------------------------------
-- -------------------+ #bản quyền tài sản "Andrew Young" #link tài sản
"http://www.easyexpertforex.com" #property Indicator_chart_window #property
Indicator_buffers 3 #property Indicator_color1 DeepSkyBlue #property
Indicator_color2 DeepSkyBlue #property Indicator_color3 DeepSkyBlue //---- đệm gấp
đôi ExtMapBuffer1[]; nhân đôi ExtMapBuffer2[]; nhân đôi ExtMapBuffer3[]; //+--------------
----------------------------------------- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+---
---------------------------------------------------- -------------------+ int init() { //---- chỉ báo
SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,ExtMapBuffer1);
SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,ExtMapBuffer2);
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,ExtMapBuffer3); //---- return(0); } Hãy
chuyển sự chú ý của chúng ta tới các phần tử được in đậm. Khai báo #property đặt
tham số cho bộ đệm chỉ báo của chúng tôi. Thuộc tính Indicator_chart_window vẽ chỉ
báo của chúng tôi trong cửa sổ biểu đồ chính. Nếu chúng ta đang tạo một bộ dao
động và muốn vẽ chỉ báo trong một cửa sổ riêng biệt, thì thay vào đó, chúng ta sẽ sử
dụng thuộc tính Indicator_separate_window. Thuộc tính Indicator_buffers đặt số lượng
bộ đệm cho chỉ báo của chúng tôi. Trong trường hợp này chúng tôi đang sử dụng ba
bộ đệm. Thuộc tính Indicator_color đặt màu của cả ba dòng thành DeepSkyBlue. 147
www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN NGHIỆP Tiếp theo là các khai báo
cho mảng bộ đệm của chúng ta. Chúng tôi có ba bộ đệm có tên ExtMapBuffer(1-3).
Chúng tôi sẽ sớm thay đổi các mã định danh mảng này thành một cái gì đó mang tính
mô tả hơn. Hàm init() là nơi chúng ta đặt các thuộc tính cho bộ đệm chỉ báo của mình.
SetIndexBuffer() liên kết một mảng bộ đệm với một chỉ mục bộ đệm. Chỉ mục bộ đệm
là những gì chúng tôi đề cập đến khi đặt thuộc tính cho một dòng chỉ báo cũng như
khi chúng tôi gọi một dòng chỉ báo từ EA bằng cách sử dụng hàm iCustom(). Tham số
đầu tiên là một số nguyên từ 0 đến 7 và tham số thứ hai là tên của mảng đệm. Các
thuộc tính của bản vẽ Hàm SetIndexStyle() thiết lập loại đường để vẽ, cùng với các
thuộc tính của đường đó. Mỗi dòng chỉ báo sẽ có hàm SetIndexStyle() tương ứng. Đây
là cú pháp: void SetIndexStyle(int BufferIndex, int LineType, int LineStyle = EMPTY, int
LineWidth = EMPTY, color LineColor = CLR_NONE) • BufferIndex – Chỉ mục của bộ
đệm, từ 0 đến 7. • LineType – Đặt loại bộ đệm đường để vẽ. DRAW_LINE vẽ một
đường duy nhất, DRAW_HISTOGRAM vẽ biểu đồ dọc (xem ví dụ về OsMA hoặc Bộ tạo
dao động tuyệt vời), DRAW_ARROW vẽ một biểu tượng và DRAW_NONE không vẽ
đường nào. • LineStyle – Một tham số tùy chọn cho biết kiểu vẽ. Được sử dụng chủ
yếu cho các dòng loại DRAW_LINE. Theo mặc định, một đường liền nét được vẽ
(STYLE_SOLID). Bạn cũng có thể vẽ các đường đứt nét (STYLE_DASH) và chấm
(STYLE_DOT). • LineWidth – Một tham số tùy chọn cho biết chiều rộng của đường tính
bằng pixel. Giá trị mặc định là 1. • LineColor – Một tham số tùy chọn cho biết màu của
đường. Nếu bạn sử dụng trình hướng dẫn, màu sẽ được đặt bằng cách sử dụng khai
báo #property, nhưng bạn cũng có thể đặt màu ở đây. Nếu bạn đang sử dụng
DRAW_ARROW làm LineType, hàm SetArrow() cho phép bạn đặt ký hiệu phông chữ
Wingdings để vẽ trên biểu đồ. Tham số đầu tiên là chỉ mục bộ đệm và tham số thứ hai
là hằng số nguyên biểu thị ký hiệu cần vẽ. Các ký hiệu có thể được tìm thấy trong
Tham chiếu MQL trong Hằng số tiêu chuẩn – Mã mũi tên. Bạn có thể muốn thêm mô
tả cho các dòng chỉ báo sẽ được hiển thị trong chú giải công cụ hoặc trong cửa sổ dữ
liệu. Để thực hiện việc này, hãy sử dụng hàm SetIndexLabel(). Tham số đầu tiên là chỉ
mục bộ đệm và tham số thứ hai là mô tả văn bản. Chúng tôi sẽ sớm thêm những thứ
này vào chỉ báo của chúng tôi. 148 www.ZTCprep.com Các chỉ báo và tập lệnh tùy
chỉnh Nếu chỉ báo của bạn được vẽ trong một cửa sổ riêng biệt (chẳng hạn như bộ
dao động) và bạn muốn thêm các mức để biểu thị mức quá mua hoặc quá bán (chẳng
hạn như trong các chỉ báo Stochastic hoặc RSI) hoặc mức 0 (chẳng hạn như trong chỉ
báo CCI), bạn có thể sử dụng các hàm SetLevelStyle() và SetLevelValue(). Xem Tham
chiếu MQL trong Chỉ báo tùy chỉnh để biết thêm thông tin. Bạn cũng có thể muốn chỉ
định một tên chỉ báo ngắn gọn sẽ được hiển thị ở góc trên cùng bên trái của cửa sổ
chỉ báo. Sử dụng hàm IndicatorShortName() để đặt giá trị này. Tham số duy nhất là
một chuỗi văn bản sẽ xuất hiện ở góc trên bên trái của cửa sổ chỉ báo, cũng như trong
cửa sổ dữ liệu. Sử dụng tên bộ đệm mô tả Đây là mã chỉ báo được cập nhật của chúng
tôi. Lưu ý rằng chúng tôi đã đổi tên các mảng đệm để mang tính mô tả rõ hơn về chức
năng thực tế của chúng. Chúng tôi đã thay đổi tham số thứ hai của hàm
SetIndexBuffer() để phản ánh tên bộ đệm mới. Chúng tôi cũng đã thêm
SetIndexLabel() cho mỗi dòng để hiển thị tên mô tả trong Cửa sổ dữ liệu. //---- đệm đôi
EMA[]; gấp đôi UpperBand[]; gấp đôi LowerBand[]; //+-----------------------------------------------------
-- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+------------------------------------------
------------- -------------------+ int init() { //---- chỉ báo SetIndexStyle(0,DRAW_LINE);
SetIndexBuffer(0,EMA); SetIndexLabel(0,"EMA"); SetIndexStyle(1,DRAW_LINE);
SetIndexBuffer(1,UpperBand); SetIndexLabel(1,"UpperBand");
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,LowerBand);
SetIndexLabel(2,"LowerBand"); //---- return(0); } Chúng tôi đã đổi tên các mảng vùng
đệm từ tên mặc định (ExtMapBuffer) thành tên mang tính mô tả hơn. EMA[] sẽ là vùng
đệm của chúng tôi cho đường trung tâm và UpperBand[] và LowerBand[] sẽ lần lượt là
dải trên và dải dưới. 149 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Các
hàm SetIndexBuffer() liên kết các chỉ mục bộ đệm với mảng bộ đệm của chúng ta.
EMA là 0, UpperBand là 1 và LowerBand là 2. Lưu ý rằng các dấu ngoặc nhọn bị loại
khỏi tên định danh mảng cho tham số SetIndexBuffer() thứ hai. Các hàm
SetIndexLabel() đặt tên mô tả cho từng vùng đệm chỉ báo. Trong trường hợp này, tên
dòng giống với tên định danh của chúng tôi. Chúng sẽ xuất hiện trên chú giải công cụ
chuột cũng như trong Cửa sổ dữ liệu. Nếu một lập trình viên khác quyết định sử dụng
chỉ báo này trong chuyên gia cố vấn, thì định dạng ở trên sẽ làm rõ chính xác chỉ mục
bộ đệm chỉ báo nào họ nên sử dụng cho mỗi dòng. Hàm start() của chỉ báo Trình
hướng dẫn chỉ chèn một biểu thức vào hàm start(): int count_bars =
IndicatorCounted(); IndicatorCounted() trả về số thanh trên biểu đồ mà chỉ báo đã tính
toán. Khi EA được khởi động lần đầu tiên, giá trị này sẽ là 0. Chỉ báo sẽ được tính cho
mọi thanh trên biểu đồ. Trên các thanh tiếp theo, chúng ta sẽ kiểm tra hàm
IndicatorCounted() để xem có bao nhiêu thanh đã được tính toán, từ đó chúng ta sẽ
biết chính xác có bao nhiêu thanh mới cần tính toán. Việc tính toán chỉ báo của chúng
tôi sẽ diễn ra bên trong vòng lặp for. Điểm bắt đầu sẽ là thanh chưa được tính toán
đầu tiên và điểm kết thúc sẽ là thanh hiện tại. Chúng ta sẽ so sánh giá trị của
IndicatorCounted() với biến Bars được xác định trước, biến này trả về số lượng thanh
trên biểu đồ hiện tại. Điều này sẽ xác định điểm khởi đầu của chúng tôi. Đây là mã cho
vòng lặp for: int count_bars = IndicatorCounted(); if(counted_bars > 0) count_bars--; int
CalculateBars = Thanh - count_bars; for(int Count = CalculateBars; Count >= 0; Count-
-) { // Tính toán chỉ báo } Câu lệnh if đầu tiên sẽ giảm giá trị của count_bars đi 1 khi
tính toán các thanh mới. Chúng tôi sẽ luôn tính toán ít nhất hai thanh trước đó. Điều
này là do điều kiện có thể không tính được dấu tích cuối cùng của thanh trong một số
trường hợp. Tiếp theo, chúng tôi xác định số lượng thanh cần tính toán bằng cách trừ
count_bars khỏi biến Bars được xác định trước. Điều này được lưu trữ trong biến
CalculateBars. 150 www.ZTCprep.com Chỉ báo và Tập lệnh Tùy chỉnh Trong vòng lặp
for của chúng tôi, biến tăng Count được đặt thành giá trị của CalculateBars, điều kiện
để kết thúc là khi Count nhỏ hơn 0 và biến Count được giảm dần sau mỗi lần lặp. Điều
này sẽ tính toán từng thanh trên biểu đồ từ trái sang phải. Đây là mã để tính Dải
Bollinger của chúng tôi. Chúng ta sẽ khai báo biến ngoài BandsPeriod ở đầu tệp. Vòng
lặp for là vòng lặp chúng ta đã tạo ở trên: // Các tham số bên ngoài extern int
BandsPeriod = 20; // hàm start() for(int Count = CalculateBars; Count >= 0; Count--) {
EMA[Count] = iMA(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); UpperBand[Count] = EMA[Count]
+ StdDev; LowerBand[Count] = EMA[Count] - StdDev; } Đầu tiên, chúng ta gọi chỉ báo
Trung bình trượt tích hợp bằng hàm iMA() và gán giá trị trả về cho EMA[Count]. Lưu ý
rằng chỉ mục mảng và tham số Shift cho chỉ báo trung bình động đều sử dụng giá trị
Đếm hiện tại. Tiếp theo, chúng tôi gọi chỉ báo Độ lệch chuẩn bằng iStdDev(). Để tính
dải trên, tất cả những gì chúng ta cần làm là cộng độ lệch chuẩn vào đường trung bình
động. Điều này được lưu trữ trong mảng đệm UpperBand[]. Để tính LowerBand[],
chúng tôi trừ đi độ lệch chuẩn khỏi mức trung bình động. Hãy mở rộng chỉ báo của
chúng tôi thêm một chút bằng cách cung cấp cho nó đầy đủ các cài đặt. Chúng tôi sẽ
thêm cài đặt để điều chỉnh dịch chuyển kỳ hạn, phương pháp trung bình động và các
tham số giá được áp dụng cũng như điều chỉnh độ lệch chuẩn: // Tham số bên ngoài
extern int BandsPeriod = 20; extern int BandsShift = 0; extern int BandsMethod = 1;
extern int BandsPrice = 0; extern int Độ lệch = 1; 151 www.ZTCprep.com LẬP TRÌNH
TƯ VẤN CHUYÊN NGHIỆP // start() function for(int Count = CalculateBars; Count >= 0;
Count--) { EMA[Count] =
iMA(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice ,Đếm); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice,Count);
UpperBand[Count] = EMA[Count] + (StdDev * Độ lệch); LowerBand[Count] =
EMA[Count] - (StdDev * Độ lệch); } Chúng tôi đã thêm các biến bên ngoài để điều chỉnh
các tham số còn lại cho hàm iMA() và iStdDev(). Chúng tôi cũng đã thêm một tham số
để điều chỉnh số lượng độ lệch chuẩn. Để tính toán điều này, chúng tôi chỉ cần nhân
StdDev với Độ lệch. Bây giờ chúng ta có chỉ báo Dải bollinger có thể điều chỉnh hoàn
toàn, linh hoạt hơn chỉ báo MetaTrader tiêu chuẩn. Mã đầy đủ được liệt kê trong Phụ
lục E. Bạn có thể làm được nhiều việc hơn với các chỉ báo tùy chỉnh thay vì chỉ tính
toán lại các chỉ báo tích hợp. Tùy thuộc vào trình độ kiến thức toán học của bạn, bạn
có thể mã hóa các chỉ báo không có trong MetaTrader hoặc thậm chí tạo chỉ báo của
riêng bạn. Bạn cũng có thể vẽ và thao tác các đối tượng. Nếu bạn muốn tìm hiểu thêm
về cách tạo chỉ báo tùy chỉnh, hãy xem chủ đề Tham khảo MQL Chỉ báo tùy chỉnh,
Hàm đối tượng và Toán & Lượng giác. Tập lệnh Tập lệnh là một chương trình MQL chỉ
chạy một lần khi nó được đính kèm lần đầu tiên vào biểu đồ. Tập lệnh có thể được sử
dụng để tự động hóa một loạt hành động giao dịch, chẳng hạn như đóng tất cả các
lệnh trên biểu đồ hoặc gửi lệnh chờ xử lý. Một số tập lệnh, chẳng hạn như tập lệnh
Period_converter đi kèm với MetaTrader, có thể vẽ lại biểu đồ dựa trên khoảng thời
gian tùy chỉnh. Tệp mã nguồn tập lệnh phải có chỉ thị thuộc tính show_confirm hoặc
show_inputs. Thuộc tính show_confirm nhắc người dùng xác nhận hoạt động của tập
lệnh, trong khi show_inputs hiển thị hộp thoại thuộc tính tập lệnh. #property
show_confirm // hiển thị hộp thoại xác nhận #property show_inputs // hiển thị hộp
thoại thuộc tính Nếu tập lệnh của bạn có các tham số cần được điều chỉnh, hãy sử
dụng thuộc tính show_inputs. Nếu không, hãy sử dụng show_confirm. 152
www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Cũng giống như các chỉ báo và
cố vấn chuyên gia, các tập lệnh sử dụng các hàm init(), deinit() và start(). Hãy nhớ
rằng mỗi hàm sẽ chỉ được chạy một lần – init() và start() khi tập lệnh được khởi động
và deinit() khi tập lệnh bị xóa. Bạn có thể đính kèm một tập lệnh vào biểu đồ tại một
thời điểm. MetaTrader đi kèm với một số tập lệnh mẫu. Tất cả các tập lệnh được lưu
trong thư mục \experts\scripts. 153 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục A Cố vấn chuyên gia đơn giản Đây là chuyên gia cố vấn đơn
giản từ chương 2. #property bản quyền "Andrew Young" // Biến ngoài extern double
LotSize = 0.1; StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern
int Trượt = 5; extern int MagicNumber = 123; extern int FastMAPeriod = 10; extern int
SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int BánTicket; điểm sử dụng gấp
đôi; int UseTrượt trượt; // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol());
UseSlippage = GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường
trung bình động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi
SlowMA = iMA(NULL,0,SlowMAPeriod,0,0,0,0); 154 www.ZTCprep.com Phụ lục A //
Lệnh mua if(FastMA > SlowMA && BuyTicket == 0) {
OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0 &&
SellTicket > 0) { double CloseLots = OrderLots(); gấp đôi Giá đóng cửa = Hỏi; bool Đã
đóng = OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } double
OpenPrice = Hỏi; // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss
= OpenPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit =
OpenPrice + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Order",MagicNumber,0,Green); BánTicket = 0; } //
Lệnh bán if(FastMA < SlowMA && SellTicket == 0) {
OrderSelect(BuyTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0 && BuyTicket >
0) { CloseLots = OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } OpenPrice = Giá thầu;
if(StopLoss > 0) double SellStopLoss = OpenPrice + (StopLoss * UsePoint);
if(TakeProfit > 0) double SellTakeProfit = OpenPrice - (TakeProfit * UsePoint);
SellTicket = OrderSend(Symbol(),OP_SELL,LotSize,OpenPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Sell Order",MagicNumber,0,Red); MuaVé = 0; } 155
www.ZTCprep.com CHUYÊN GIA LẬP TRÌNH TƯ VẤN return(0); } // Hàm điểm Pip
nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits = MarketInfo(Tiền tệ,MODE_DIGITS);
if(CalcDigits == 2 || CalcDigits == 3) gấp đôi CalcPoint = 0,01; khác nếu(CalcDigits == 4
|| CalcDigits == 5) CalcPoint = 0,0001; return(CalcPoint); } // Lấy hàm trượt giá int
GetSlippage(string Money, int SlippagePips) { int CalcDigits =
MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 4) double
CalcSlippage = SlippagePips; else if(CalcDigits == 3 || CalcDigits == 5) CalcSlippage =
SlippagePips * 10; return(CalcSlippage); } Chuyên gia cố vấn đơn giản với các lệnh
đang chờ xử lý Đây là chuyên gia cố vấn đơn giản sử dụng các lệnh dừng đang chờ xử
lý: #property Copyright "Andrew Young" // Biến bên ngoài extern double LotSize = 0.1;
StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern int
PendingPips = 10; extern int Trượt = 5; extern int MagicNumber = 123; extern int
FastMAPeriod = 10; extern int SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int
BánTicket; điểm sử dụng gấp đôi; int UseTrượt trượt; 156 www.ZTCprep.com Phụ lục
A // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol()); UseSlippage =
GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường trung bình
động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,0); // Lệnh mua if(FastMA > SlowMA && BuyTicket ==
0) { OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0
&& SellTicket > 0 && OrderType() == OP_SELL) { double CloseLots = OrderLots(); gấp
đôi Giá đóng cửa = Hỏi; bool Đã đóng =
OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } // Xóa đơn hàng khác
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_SELLSTOP) { bool
Đã xóa = OrderDelete(SellTicket,Red); } double PendingPrice = Cao[0] + (PendingPips *
UsePoint); // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss =
PendingPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit = Đang
chờ xử lý + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Stop Order",MagicNumber,0,Green); BánTicket = 0; }
157 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA // Bán lệnh if(FastMA <
SlowMA && SellTicket == 0) { OrderSelect(BuyTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0 && BuyTicket > 0 && OrderType() == OP_BUY) { CloseLots =
OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } else
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_BUYSTOP) { Đã xóa
= OrderDelete(SellTicket,Red); } Giá đang chờ xử lý = Thấp[0] - (PendingPips *
UsePoint); if(StopLoss > 0) double SellStopLoss = Giá đang chờ xử lý + (StopLoss *
UsePoint); if(TakeProfit > 0) double SellTakeProfit = Đang chờ xử lý - (TakeProfit *
UsePoint); SellTicket =
OrderSend(Symbol(),OP_SELLSTOP,LotSize,PendingPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Lệnh dừng bán",MagicNumber,0,Red); MuaVé = 0; } trả
về(0); } // Hàm điểm Pip nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits =
MarketInfo(Tiền tệ,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 3) gấp đôi
CalcPoint = 0,01; khác nếu(CalcDigits == 4 || CalcDigits == 5) CalcPoint = 0,0001;
return(CalcPoint); } // Lấy hàm trượt giá int GetSlippage(string Money, int
SlippagePips) { int CalcDigits = MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2
|| CalcDigits == 4) double CalcSlippage = SlippagePips; else if(CalcDigits == 3 ||
CalcDigits == 5) CalcSlippage = SlippagePips * 10; return(CalcSlippage); } 158
www.ZTCprep.com Phụ lục A 159 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục B Cố vấn chuyên gia nâng cao Đây là chuyên gia cố vấn với
các tính năng nâng cao từ chương 3. #property Copyright "Andrew Young" #include và
dd là ngày có hai chữ số. Bạn có thể xem nhật ký trong Notepad hoặc bất kỳ trình
soạn thảo văn bản nào. Hãy minh họa một ví dụ về cách bạn có thể sử dụng nhật ký
để xác định vấn đề lập trình. Mã bên dưới có lỗi và nó không hoạt động như chúng tôi
mong đợi. Để có thể chẩn đoán được vấn đề, 141 www.ZTCprep.com CHƯƠNG TRÌNH
TƯ VẤN CHUYÊN NGHIỆP chúng ta cần kiểm tra đầu vào hoặc đầu ra của hàm. Hãy
tạo một câu lệnh Print() và in nội dung của tất cả các biến có liên quan vào nhật ký.
Chúng tôi sẽ chạy EA trong Trình kiểm tra chiến lược, chỉ sử dụng Giá mở làm mô hình
thử nghiệm. Hãy đảm bảo rằng bạn đang thử nghiệm EA trong một khoảng thời gian
đủ dài để EA có thể đặt đủ giao dịch cho chúng tôi phân tích. Nếu bạn cần kiểm tra giá
trên biểu đồ, hãy nhấn nút Mở biểu đồ để mở biểu đồ hiển thị các giao dịch được mô
phỏng. Tiếp theo, chúng ta sẽ chuyển đến tab Nhật ký và kiểm tra thông tin chúng ta
cần. Nếu chúng ta cần xem toàn bộ nhật ký hoặc nếu có giao dịch không hiển thị trong
tab Nhật ký, chúng ta có thể nhấp chuột phải và chọn Mở từ menu bật lên và mở tệp
nhật ký trực tiếp. Mã này đang gây ra lỗi 130: "điểm dừng không hợp lệ" mỗi khi chúng
tôi đặt lệnh mua. Chúng tôi biết rằng lỗi 130 có nghĩa là lệnh dừng lỗ hoặc chốt lãi
không chính xác. Bạn có thể xác định được lỗi không? if(Close[0] > MA && BuyTicket
== 0) { double Openprice = Hỏi; gấp đôi BuyStopLoss = OpenPrice + (StopLoss *
UsePoint); gấp đôi BuyTakeProfit = OpenPrice + (TakeProfit * UsePoint); BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,Openprice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Mua đơn hàng",MagicNumber,0,Green); BánTicket = 0; }
Chúng ta sẽ sử dụng hàm Print() để xác minh các tham số đang được truyền cho hàm
OrderSend(). Chúng ta sẽ tập trung vào giá mở lệnh, mức dừng lỗ và chốt lời.
Print("Giá:"+OpenPrice+" Dừng:"+BuyStopLoss+" Lợi nhuận:"+BuyTakeProfit); Đây là
kết quả khi chúng tôi chạy EA trong trình thử nghiệm chiến lược. Giả sử mức dừng lỗ
và chốt lời là 50 pips: 11:52:12 2009.11.02 02:00 Ví dụ EURUSD,H1: Lỗi gửi lệnh 130
11:52:12 2009.11.02 02:00 Ví dụ EURUSD,H1: Giá:1.47340000 Dừng:1.47840000 Lợi
nhuận:1.47840000 Chúng tôi biết rằng mức dừng lỗ phải thấp hơn giá mở cửa cho
một lệnh mua. Ở đây, nó cao hơn giá. Trên thực tế, nó có cùng mức giá với mức chốt
lời. Xem nhanh mã của chúng tôi và chúng tôi nhận ra rằng chúng tôi đã vô tình chèn
dấu cộng vào phương trình dừng lỗ mua. Đây là mã chính xác: 142 www.ZTCprep.com
Mẹo và thủ thuật nhân đôi BuyStopLoss = OpenPrice - (StopLoss * UsePoint); Nếu bạn
nhận được thông báo lỗi khi cố gắng đặt, đóng hoặc sửa đổi đơn hàng, hãy tập trung
nỗ lực vào vấn đề được chỉ ra bởi thông báo lỗi. Dưới đây là một số thông báo lỗi phổ
biến nhất do lỗi lập trình gây ra: • Lỗi 129: Giá không hợp lệ – Giá mở cửa không hợp
lệ. Đối với các lệnh thị trường, hãy đảm bảo giá Mua hoặc Bán hiện tại đang được
thông qua, tùy theo loại lệnh. Đối với các lệnh đang chờ xử lý, hãy đảm bảo giá cao
hơn hoặc thấp hơn giá hiện tại, theo yêu cầu của loại lệnh. Đồng thời kiểm tra xem giá
lệnh chờ xử lý có quá gần với giá hiện tại hay không (tức là nằm trong mức dừng). • Lỗi
130: Điểm dừng không hợp lệ – Giá dừng lỗ hoặc chốt lãi không chính xác. Kiểm tra
xem giá dừng lỗ và chốt lời được đặt ở trên hay dưới giá hiện tại, tùy thuộc vào loại
lệnh là mua hay bán. Đồng thời kiểm tra xem giá dừng lỗ hoặc chốt lãi không quá gần
với giá hiện tại (tức là nằm trong mức dừng). • Lỗi 131: Khối lượng giao dịch không
hợp lệ – Kích thước lô không chính xác. Đảm bảo rằng kích thước lô không vượt quá
mức tối thiểu hoặc tối đa của nhà môi giới và kích thước lô được chuẩn hóa thành giá
trị bước chính xác (0,1 hoặc 0,01 trên hầu hết các nhà môi giới). Bạn có thể tìm thấy
mô tả của tất cả các thông báo lỗi trong Tham chiếu MQL trong Hằng số tiêu chuẩn –
Mã lỗi. Nếu bạn cần hỗ trợ thêm về lỗi mà bạn đang gặp phải, hãy kiểm tra các diễn
đàn tại MQL4.com. Khắc phục sự cố các lỗi giao dịch không liên tục Trong khi hầu hết
các lỗi nghiêm trọng có thể được tìm thấy đơn giản bằng cách kiểm tra lại, những lỗi
khác sẽ chỉ xảy ra trong quá trình giao dịch theo thời gian thực. Lỗi logic có thể dẫn
đến giao dịch không được đặt chính xác và bạn có thể phải mất một chút nỗ lực để
xác định những lỗi này. Nếu có giao dịch được đặt không chính xác trong quá trình
giao dịch demo hoặc giao dịch trực tiếp, chúng tôi cần nhiều thông tin cần thiết để
khắc phục sự cố. Chúng tôi sẽ thêm một tính năng tùy chọn để ghi nhật ký thông tin
trạng thái và giao dịch theo thời gian thực để chúng tôi có bản ghi về nó khi khắc phục
sự cố giao dịch. Chúng tôi sẽ sử dụng câu lệnh Print() như trước đây nhưng chúng tôi
sẽ ghi lại các giá trị chỉ báo, giá cả – bất kỳ thông tin nào hữu ích trong việc gỡ lỗi.
Chúng tôi cũng sẽ thêm một biến bên ngoài để bật và tắt đăng nhập. // Biến ngoài
extern bool Debug = true; 143 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN
NGHIỆP // Đặt ở gần cuối hàm start() if(Debug == true)
Print(StringConcatenate("Bid:",Bid," Ask:",Ask," MA:" ,MA, " MuaTicket:",BuyTicket,"
SellTicket:",SellTicket)); Đoạn mã trên sẽ ghi lại thông tin về giá và chỉ báo cũng như
nội dung của các biến BuyTicket và SellTicket. Nếu có bất kỳ câu hỏi nào về cách mở
giao dịch hoặc tại sao giao dịch không được mở, nhật ký tại thời điểm cụ thể đó sẽ
hiển thị trạng thái của tất cả các điều kiện giao dịch có liên quan. Bạn có thể bật và tắt
đăng nhập bằng biến ngoài Debug. Câu lệnh debug Print() phải được đặt ở gần cuối
hàm start(), sau tất cả các hàm giao dịch. Nếu bạn đang sử dụng bộ hẹn giờ và/hoặc
tính năng thực thi khi mở thanh, hãy đặt câu lệnh debug Print() bên trong khối bộ hẹn
giờ để nó chỉ chạy khi cần thiết. Nếu không, dòng gỡ lỗi sẽ in ra nhật ký sau mỗi tích
tắc, điều này có thể tạo ra một tệp nhật ký lớn. Sửa lỗi biên dịch Khi bạn biên dịch
Expert Advisor, trình biên dịch sẽ kiểm tra cú pháp đúng và đảm bảo rằng tất cả các
hàm và biến tùy chỉnh đã được khai báo đúng. Nếu bạn bỏ sót nội dung nào đó, trình
biên dịch sẽ dừng và mọi lỗi biên dịch sẽ xuất hiện trong tab Lỗi trong cửa sổ Hộp
công cụ. Khi gặp phải một danh sách dài các lỗi biên dịch, hãy luôn bắt đầu từ lỗi đầu
tiên. Bấm đúp vào lỗi trong danh sách và trình soạn thảo sẽ chuyển trực tiếp đến dòng
có lỗi. Sửa lỗi và biên dịch lại. Đôi khi một lỗi cú pháp đơn giản sẽ dẫn đến một số lỗi
không liên quan, mặc dù chỉ có lỗi đầu tiên là hợp lệ. Đây là danh sách các lỗi biên
dịch thường gặp và cách khắc phục: • Biến không được xác định – Bạn quên khai báo
một biến có kiểu dữ liệu. Nếu đó là biến toàn cục hoặc biến ngoài, hãy khai báo nó ở
đầu tệp. Nếu đó là biến cục bộ, hãy tìm lần xuất hiện đầu tiên và đặt phần khai báo
kiểu dữ liệu trước biến đó. Nếu không, hãy kiểm tra chính tả hoặc kiểu chữ
(hoa/thường) của tên biến. • Biến đã được xác định – Bạn đã khai báo cùng một biến
hai lần. Loại bỏ phần khai báo kiểu dữ liệu khỏi tất cả các khai báo biến trùng lặp. •
Hàm không được xác định – Nếu hàm được đề cập nằm trong tệp bao gồm hoặc thư
viện, hãy đảm bảo rằng lệnh #include hoặc #import được đặt ở đầu tệp và đúng. Nếu
không, hãy kiểm tra chính tả hoặc kiểu chữ của tên hàm và đảm bảo rằng nó tồn tại
trong tệp hiện tại hoặc trong tệp bao gồm hoặc thư viện có liên quan. 144
www.ZTCprep.com Mẹo và thủ thuật • Phép gán bất hợp pháp được sử dụng – Điều
này thường liên quan đến dấu bằng (=). Hãy nhớ rằng một dấu bằng duy nhất dành
cho phép gán biến và hai dấu bằng (==) là toán tử so sánh. Sửa toán tử gán thành toán
tử so sánh thích hợp. • Phép gán dự kiến – Điều này thường liên quan đến toán tử so
sánh "bằng" (==). Bạn đã sử dụng hai dấu bằng thay vì một trong phép gán biến. Sửa
toán tử thành một dấu bằng duy nhất. • Dấu ngoặc đơn bên phải không cân bằng –
Điều này thường xảy ra trong câu lệnh if khi sử dụng dấu ngoặc đơn lồng nhau. Đi đến
dòng được chỉ ra bởi lỗi đầu tiên và chèn dấu ngoặc đơn bên trái vào vị trí thích hợp. •
Dấu ngoặc trái không cân bằng – Đây là một dấu ngoặc đơn khó. Lỗi thường xảy ra ở
cuối dòng chương trình. Về cơ bản bạn đã quên dấu ngoặc đơn ở đâu đó. Kiểm tra kỹ
mã bạn đã chỉnh sửa gần đây và tìm dấu ngoặc đơn bên phải bị thiếu. Bạn có thể phải
nhận xét các dòng mã để xác định vấn đề. • Đếm tham số sai – Bạn có quá ít hoặc
quá nhiều đối số trong một hàm. Kiểm tra kỹ cú pháp hàm trong Tham chiếu MQL và
sửa các đối số. • Cần có dấu chấm phẩy – Có thể bạn đã quên đặt dấu chấm phẩy ở
cuối dòng. Đặt dấu chấm phẩy ở cuối dòng trước. Lưu ý rằng thiếu dấu chấm phẩy
cũng có thể gây ra bất kỳ lỗi nào ở trên, vì vậy hãy nhớ đặt các dấu chấm phẩy đó! 145
www.ZTCprep.com CHƯƠNG TRÌNH CỐ VẤN CHUYÊN GIA Chương 9 Các chỉ báo và
tập lệnh tùy chỉnh Không có cuốn sách nào về MQL sẽ hoàn chỉnh nếu không đề cập
đến các chỉ báo và tập lệnh tùy chỉnh. Các chỉ báo tích hợp trong MetaTrader khá hạn
chế, nhưng may mắn thay MQL cho phép các lập trình viên tạo các chỉ báo của riêng
họ. Nếu bạn đang tìm kiếm một chỉ báo phổ biến không có trong MT4 thì rất có thể ai
đó đã tạo một chỉ báo đó. Chương này sẽ là tổng quan cơ bản về việc tạo chỉ báo tùy
chỉnh. Hầu hết các chỉ báo đều sử dụng các công thức toán học phức tạp và đó là lĩnh
vực của các lập trình viên giàu kinh nghiệm hơn. Tuy nhiên, một chỉ báo không cần
phải phức tạp. Chúng ta sẽ tạo một chỉ báo tùy chỉnh trong chương này chỉ sử dụng
một vài dòng mã. Bộ đệm Bộ đệm là mảng lưu trữ các giá trị chỉ báo và phép tính. Một
chỉ báo tùy chỉnh có thể có tối đa 8 bộ đệm. Bộ đệm sử dụng các chỉ mục, giống như
mảng và có phạm vi từ 0 đến 7. Khi bạn gọi một chỉ báo tùy chỉnh trong chuyên gia tư
vấn bằng cách sử dụng hàm iCustom(), tham số tiếp theo trong hàm là bộ đệm chỉ
báo. Để tìm vùng đệm thích hợp cho dòng chỉ báo, bạn thường kiểm tra mã nguồn, nếu
có. Nếu mã nguồn được định dạng rõ ràng với các tên biến mô tả, bạn sẽ có thể xác
định bộ đệm thích hợp khá dễ dàng. Chúng ta sẽ đề cập đến cách đặt tên thích hợp
cho bộ đệm chỉ báo trong chương này. Tạo chỉ báo tùy chỉnh Hãy xây dựng một chỉ
báo tùy chỉnh bằng cách sử dụng hai chỉ báo MetaTrader tích hợp để tính toán các
đường của chúng tôi. Chúng ta sẽ xây dựng một chỉ báo Dải Bollinger đã được sửa
đổi. Dải Bollinger bao gồm 3 đường – đường trung tâm là đường trung bình động đơn
giản, cùng với đường trên và đường dưới có giá trị được xác định bằng độ lệch chuẩn.
Chúng ta có thể tạo chỉ báo Dải bollinger của riêng mình bằng cách sử dụng các chỉ
báo Trung bình trượt và Độ lệch chuẩn. Chúng tôi muốn tạo một chỉ báo sử dụng
đường trung bình động hàm mũ để tính toán các đường, trái ngược với đường trung
bình động đơn giản. 146 www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Chúng
tôi bắt đầu bằng cách sử dụng trình hướng dẫn để tạo tệp chỉ báo của mình. Chọn Mới
từ menu Tệp hoặc thanh công cụ để mở trình hướng dẫn và tạo chỉ báo tùy chỉnh. Điền
tên chỉ báo và thêm thông số nếu muốn. Trên trang cuối cùng, chúng tôi đã thêm ba
dòng chỉ báo cùng màu. Đây là kết quả của thuật sĩ. Hiện tại chúng ta đã bỏ qua hàm
start(): //+--------------------------------- --------------------------------+ //| EMA Bollinger.mq4 | //|
Andrew trẻ | //| http://www.easyexpertforex.com | //+-----------------------------------------------------
-- -------------------+ #bản quyền tài sản "Andrew Young" #link tài sản
"http://www.easyexpertforex.com" #property Indicator_chart_window #property
Indicator_buffers 3 #property Indicator_color1 DeepSkyBlue #property
Indicator_color2 DeepSkyBlue #property Indicator_color3 DeepSkyBlue //---- đệm gấp
đôi ExtMapBuffer1[]; nhân đôi ExtMapBuffer2[]; nhân đôi ExtMapBuffer3[]; //+--------------
----------------------------------------- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+---
---------------------------------------------------- -------------------+ int init() { //---- chỉ báo
SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,ExtMapBuffer1);
SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,ExtMapBuffer2);
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,ExtMapBuffer3); //---- return(0); } Hãy
chuyển sự chú ý của chúng ta tới các phần tử được in đậm. Khai báo #property đặt
tham số cho bộ đệm chỉ báo của chúng tôi. Thuộc tính Indicator_chart_window vẽ chỉ
báo của chúng tôi trong cửa sổ biểu đồ chính. Nếu chúng ta đang tạo một bộ dao
động và muốn vẽ chỉ báo trong một cửa sổ riêng biệt, thì thay vào đó, chúng ta sẽ sử
dụng thuộc tính Indicator_separate_window. Thuộc tính Indicator_buffers đặt số lượng
bộ đệm cho chỉ báo của chúng tôi. Trong trường hợp này chúng tôi đang sử dụng ba
bộ đệm. Thuộc tính Indicator_color đặt màu của cả ba dòng thành DeepSkyBlue. 147
www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN NGHIỆP Tiếp theo là các khai báo
cho mảng bộ đệm của chúng ta. Chúng tôi có ba bộ đệm có tên ExtMapBuffer(1-3).
Chúng tôi sẽ sớm thay đổi các mã định danh mảng này thành một cái gì đó mang tính
mô tả hơn. Hàm init() là nơi chúng ta đặt các thuộc tính cho bộ đệm chỉ báo của mình.
SetIndexBuffer() liên kết một mảng bộ đệm với một chỉ mục bộ đệm. Chỉ mục bộ đệm
là những gì chúng tôi đề cập đến khi đặt thuộc tính cho một dòng chỉ báo cũng như
khi chúng tôi gọi một dòng chỉ báo từ EA bằng cách sử dụng hàm iCustom(). Tham số
đầu tiên là một số nguyên từ 0 đến 7 và tham số thứ hai là tên của mảng đệm. Các
thuộc tính của bản vẽ Hàm SetIndexStyle() thiết lập loại đường để vẽ, cùng với các
thuộc tính của đường đó. Mỗi dòng chỉ báo sẽ có hàm SetIndexStyle() tương ứng. Đây
là cú pháp: void SetIndexStyle(int BufferIndex, int LineType, int LineStyle = EMPTY, int
LineWidth = EMPTY, color LineColor = CLR_NONE) • BufferIndex – Chỉ mục của bộ
đệm, từ 0 đến 7. • LineType – Đặt loại bộ đệm đường để vẽ. DRAW_LINE vẽ một
đường duy nhất, DRAW_HISTOGRAM vẽ biểu đồ dọc (xem ví dụ về OsMA hoặc Bộ tạo
dao động tuyệt vời), DRAW_ARROW vẽ một biểu tượng và DRAW_NONE không vẽ
đường nào. • LineStyle – Một tham số tùy chọn cho biết kiểu vẽ. Được sử dụng chủ
yếu cho các dòng loại DRAW_LINE. Theo mặc định, một đường liền nét được vẽ
(STYLE_SOLID). Bạn cũng có thể vẽ các đường đứt nét (STYLE_DASH) và chấm
(STYLE_DOT). • LineWidth – Một tham số tùy chọn cho biết chiều rộng của đường tính
bằng pixel. Giá trị mặc định là 1. • LineColor – Một tham số tùy chọn cho biết màu của
đường. Nếu bạn sử dụng trình hướng dẫn, màu sẽ được đặt bằng cách sử dụng khai
báo #property, nhưng bạn cũng có thể đặt màu ở đây. Nếu bạn đang sử dụng
DRAW_ARROW làm LineType, hàm SetArrow() cho phép bạn đặt ký hiệu phông chữ
Wingdings để vẽ trên biểu đồ. Tham số đầu tiên là chỉ mục bộ đệm và tham số thứ hai
là hằng số nguyên biểu thị ký hiệu cần vẽ. Các ký hiệu có thể được tìm thấy trong
Tham chiếu MQL trong Hằng số tiêu chuẩn – Mã mũi tên. Bạn có thể muốn thêm mô
tả cho các dòng chỉ báo sẽ được hiển thị trong chú giải công cụ hoặc trong cửa sổ dữ
liệu. Để thực hiện việc này, hãy sử dụng hàm SetIndexLabel(). Tham số đầu tiên là chỉ
mục bộ đệm và tham số thứ hai là mô tả văn bản. Chúng tôi sẽ sớm thêm những thứ
này vào chỉ báo của chúng tôi. 148 www.ZTCprep.com Các chỉ báo và tập lệnh tùy
chỉnh Nếu chỉ báo của bạn được vẽ trong một cửa sổ riêng biệt (chẳng hạn như bộ
dao động) và bạn muốn thêm các mức để biểu thị mức quá mua hoặc quá bán (chẳng
hạn như trong các chỉ báo Stochastic hoặc RSI) hoặc mức 0 (chẳng hạn như trong chỉ
báo CCI), bạn có thể sử dụng các hàm SetLevelStyle() và SetLevelValue(). Xem Tham
chiếu MQL trong Chỉ báo tùy chỉnh để biết thêm thông tin. Bạn cũng có thể muốn chỉ
định một tên chỉ báo ngắn gọn sẽ được hiển thị ở góc trên cùng bên trái của cửa sổ
chỉ báo. Sử dụng hàm IndicatorShortName() để đặt giá trị này. Tham số duy nhất là
một chuỗi văn bản sẽ xuất hiện ở góc trên bên trái của cửa sổ chỉ báo, cũng như trong
cửa sổ dữ liệu. Sử dụng tên bộ đệm mô tả Đây là mã chỉ báo được cập nhật của chúng
tôi. Lưu ý rằng chúng tôi đã đổi tên các mảng đệm để mang tính mô tả rõ hơn về chức
năng thực tế của chúng. Chúng tôi đã thay đổi tham số thứ hai của hàm
SetIndexBuffer() để phản ánh tên bộ đệm mới. Chúng tôi cũng đã thêm
SetIndexLabel() cho mỗi dòng để hiển thị tên mô tả trong Cửa sổ dữ liệu. //---- đệm đôi
EMA[]; gấp đôi UpperBand[]; gấp đôi LowerBand[]; //+-----------------------------------------------------
-- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+------------------------------------------
------------- -------------------+ int init() { //---- chỉ báo SetIndexStyle(0,DRAW_LINE);
SetIndexBuffer(0,EMA); SetIndexLabel(0,"EMA"); SetIndexStyle(1,DRAW_LINE);
SetIndexBuffer(1,UpperBand); SetIndexLabel(1,"UpperBand");
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,LowerBand);
SetIndexLabel(2,"LowerBand"); //---- return(0); } Chúng tôi đã đổi tên các mảng vùng
đệm từ tên mặc định (ExtMapBuffer) thành tên mang tính mô tả hơn. EMA[] sẽ là vùng
đệm của chúng tôi cho đường trung tâm và UpperBand[] và LowerBand[] sẽ lần lượt là
dải trên và dải dưới. 149 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Các
hàm SetIndexBuffer() liên kết các chỉ mục bộ đệm với mảng bộ đệm của chúng ta.
EMA là 0, UpperBand là 1 và LowerBand là 2. Lưu ý rằng các dấu ngoặc nhọn bị loại
khỏi tên định danh mảng cho tham số SetIndexBuffer() thứ hai. Các hàm
SetIndexLabel() đặt tên mô tả cho từng vùng đệm chỉ báo. Trong trường hợp này, tên
dòng giống với tên định danh của chúng tôi. Chúng sẽ xuất hiện trên chú giải công cụ
chuột cũng như trong Cửa sổ dữ liệu. Nếu một lập trình viên khác quyết định sử dụng
chỉ báo này trong chuyên gia cố vấn, thì định dạng ở trên sẽ làm rõ chính xác chỉ mục
bộ đệm chỉ báo nào họ nên sử dụng cho mỗi dòng. Hàm start() của chỉ báo Trình
hướng dẫn chỉ chèn một biểu thức vào hàm start(): int count_bars =
IndicatorCounted(); IndicatorCounted() trả về số thanh trên biểu đồ mà chỉ báo đã tính
toán. Khi EA được khởi động lần đầu tiên, giá trị này sẽ là 0. Chỉ báo sẽ được tính cho
mọi thanh trên biểu đồ. Trên các thanh tiếp theo, chúng ta sẽ kiểm tra hàm
IndicatorCounted() để xem có bao nhiêu thanh đã được tính toán, từ đó chúng ta sẽ
biết chính xác có bao nhiêu thanh mới cần tính toán. Việc tính toán chỉ báo của chúng
tôi sẽ diễn ra bên trong vòng lặp for. Điểm bắt đầu sẽ là thanh chưa được tính toán
đầu tiên và điểm kết thúc sẽ là thanh hiện tại. Chúng ta sẽ so sánh giá trị của
IndicatorCounted() với biến Bars được xác định trước, biến này trả về số lượng thanh
trên biểu đồ hiện tại. Điều này sẽ xác định điểm khởi đầu của chúng tôi. Đây là mã cho
vòng lặp for: int count_bars = IndicatorCounted(); if(counted_bars > 0) count_bars--; int
CalculateBars = Thanh - count_bars; for(int Count = CalculateBars; Count >= 0; Count-
-) { // Tính toán chỉ báo } Câu lệnh if đầu tiên sẽ giảm giá trị của count_bars đi 1 khi
tính toán các thanh mới. Chúng tôi sẽ luôn tính toán ít nhất hai thanh trước đó. Điều
này là do điều kiện có thể không tính được dấu tích cuối cùng của thanh trong một số
trường hợp. Tiếp theo, chúng tôi xác định số lượng thanh cần tính toán bằng cách trừ
count_bars khỏi biến Bars được xác định trước. Điều này được lưu trữ trong biến
CalculateBars. 150 www.ZTCprep.com Chỉ báo và Tập lệnh Tùy chỉnh Trong vòng lặp
for của chúng tôi, biến tăng Count được đặt thành giá trị của CalculateBars, điều kiện
để kết thúc là khi Count nhỏ hơn 0 và biến Count được giảm dần sau mỗi lần lặp. Điều
này sẽ tính toán từng thanh trên biểu đồ từ trái sang phải. Đây là mã để tính Dải
Bollinger của chúng tôi. Chúng ta sẽ khai báo biến ngoài BandsPeriod ở đầu tệp. Vòng
lặp for là vòng lặp chúng ta đã tạo ở trên: // Các tham số bên ngoài extern int
BandsPeriod = 20; // hàm start() for(int Count = CalculateBars; Count >= 0; Count--) {
EMA[Count] = iMA(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); UpperBand[Count] = EMA[Count]
+ StdDev; LowerBand[Count] = EMA[Count] - StdDev; } Đầu tiên, chúng ta gọi chỉ báo
Trung bình trượt tích hợp bằng hàm iMA() và gán giá trị trả về cho EMA[Count]. Lưu ý
rằng chỉ mục mảng và tham số Shift cho chỉ báo trung bình động đều sử dụng giá trị
Đếm hiện tại. Tiếp theo, chúng tôi gọi chỉ báo Độ lệch chuẩn bằng iStdDev(). Để tính
dải trên, tất cả những gì chúng ta cần làm là cộng độ lệch chuẩn vào đường trung bình
động. Điều này được lưu trữ trong mảng đệm UpperBand[]. Để tính LowerBand[],
chúng tôi trừ đi độ lệch chuẩn khỏi mức trung bình động. Hãy mở rộng chỉ báo của
chúng tôi thêm một chút bằng cách cung cấp cho nó đầy đủ các cài đặt. Chúng tôi sẽ
thêm cài đặt để điều chỉnh dịch chuyển kỳ hạn, phương pháp trung bình động và các
tham số giá được áp dụng cũng như điều chỉnh độ lệch chuẩn: // Tham số bên ngoài
extern int BandsPeriod = 20; extern int BandsShift = 0; extern int BandsMethod = 1;
extern int BandsPrice = 0; extern int Độ lệch = 1; 151 www.ZTCprep.com LẬP TRÌNH
TƯ VẤN CHUYÊN NGHIỆP // start() function for(int Count = CalculateBars; Count >= 0;
Count--) { EMA[Count] =
iMA(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice ,Đếm); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice,Count);
UpperBand[Count] = EMA[Count] + (StdDev * Độ lệch); LowerBand[Count] =
EMA[Count] - (StdDev * Độ lệch); } Chúng tôi đã thêm các biến bên ngoài để điều chỉnh
các tham số còn lại cho hàm iMA() và iStdDev(). Chúng tôi cũng đã thêm một tham số
để điều chỉnh số lượng độ lệch chuẩn. Để tính toán điều này, chúng tôi chỉ cần nhân
StdDev với Độ lệch. Bây giờ chúng ta có chỉ báo Dải bollinger có thể điều chỉnh hoàn
toàn, linh hoạt hơn chỉ báo MetaTrader tiêu chuẩn. Mã đầy đủ được liệt kê trong Phụ
lục E. Bạn có thể làm được nhiều việc hơn với các chỉ báo tùy chỉnh thay vì chỉ tính
toán lại các chỉ báo tích hợp. Tùy thuộc vào trình độ kiến thức toán học của bạn, bạn
có thể mã hóa các chỉ báo không có trong MetaTrader hoặc thậm chí tạo chỉ báo của
riêng bạn. Bạn cũng có thể vẽ và thao tác các đối tượng. Nếu bạn muốn tìm hiểu thêm
về cách tạo chỉ báo tùy chỉnh, hãy xem chủ đề Tham khảo MQL Chỉ báo tùy chỉnh,
Hàm đối tượng và Toán & Lượng giác. Tập lệnh Tập lệnh là một chương trình MQL chỉ
chạy một lần khi nó được đính kèm lần đầu tiên vào biểu đồ. Tập lệnh có thể được sử
dụng để tự động hóa một loạt hành động giao dịch, chẳng hạn như đóng tất cả các
lệnh trên biểu đồ hoặc gửi lệnh chờ xử lý. Một số tập lệnh, chẳng hạn như tập lệnh
Period_converter đi kèm với MetaTrader, có thể vẽ lại biểu đồ dựa trên khoảng thời
gian tùy chỉnh. Tệp mã nguồn tập lệnh phải có chỉ thị thuộc tính show_confirm hoặc
show_inputs. Thuộc tính show_confirm nhắc người dùng xác nhận hoạt động của tập
lệnh, trong khi show_inputs hiển thị hộp thoại thuộc tính tập lệnh. #property
show_confirm // hiển thị hộp thoại xác nhận #property show_inputs // hiển thị hộp
thoại thuộc tính Nếu tập lệnh của bạn có các tham số cần điều chỉnh, hãy sử dụng
thuộc tính show_inputs. Nếu không, hãy sử dụng show_confirm. 152
www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Cũng giống như các chỉ báo và
cố vấn chuyên gia, các tập lệnh sử dụng các hàm init(), deinit() và start(). Hãy nhớ
rằng mỗi hàm sẽ chỉ được chạy một lần – init() và start() khi tập lệnh được khởi động
và deinit() khi tập lệnh bị xóa. Bạn có thể đính kèm một tập lệnh vào biểu đồ tại một
thời điểm. MetaTrader đi kèm với một số tập lệnh mẫu. Tất cả các tập lệnh được lưu
trong thư mục \experts\scripts. 153 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục A Cố vấn chuyên gia đơn giản Đây là chuyên gia cố vấn đơn
giản từ chương 2. #property bản quyền "Andrew Young" // Biến ngoài extern double
LotSize = 0.1; StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern
int Trượt = 5; extern int MagicNumber = 123; extern int FastMAPeriod = 10; extern int
SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int BánTicket; điểm sử dụng gấp
đôi; int UseTrượt trượt; // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol());
UseSlippage = GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường
trung bình động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi
SlowMA = iMA(NULL,0,SlowMAPeriod,0,0,0,0); 154 www.ZTCprep.com Phụ lục A //
Lệnh mua if(FastMA > SlowMA && BuyTicket == 0) {
OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0 &&
SellTicket > 0) { double CloseLots = OrderLots(); gấp đôi Giá đóng cửa = Hỏi; bool Đã
đóng = OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } double
OpenPrice = Hỏi; // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss
= OpenPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit =
OpenPrice + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Order",MagicNumber,0,Green); BánTicket = 0; } //
Lệnh bán if(FastMA < SlowMA && SellTicket == 0) {
OrderSelect(BuyTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0 && BuyTicket >
0) { CloseLots = OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } OpenPrice = Giá thầu;
if(StopLoss > 0) double SellStopLoss = OpenPrice + (StopLoss * UsePoint);
if(TakeProfit > 0) double SellTakeProfit = OpenPrice - (TakeProfit * UsePoint);
SellTicket = OrderSend(Symbol(),OP_SELL,LotSize,OpenPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Sell Order",MagicNumber,0,Red); MuaVé = 0; } 155
www.ZTCprep.com CHUYÊN GIA LẬP TRÌNH TƯ VẤN return(0); } // Hàm điểm Pip
nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits = MarketInfo(Tiền tệ,MODE_DIGITS);
if(CalcDigits == 2 || CalcDigits == 3) gấp đôi CalcPoint = 0,01; khác nếu(CalcDigits == 4
|| CalcDigits == 5) CalcPoint = 0,0001; return(CalcPoint); } // Lấy hàm trượt giá int
GetSlippage(string Money, int SlippagePips) { int CalcDigits =
MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 4) double
CalcSlippage = SlippagePips; else if(CalcDigits == 3 || CalcDigits == 5) CalcSlippage =
SlippagePips * 10; return(CalcSlippage); } Chuyên gia cố vấn đơn giản với các lệnh
đang chờ xử lý Đây là chuyên gia cố vấn đơn giản sử dụng các lệnh dừng đang chờ xử
lý: #property Copyright "Andrew Young" // Biến bên ngoài extern double LotSize = 0.1;
StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern int
PendingPips = 10; extern int Trượt = 5; extern int MagicNumber = 123; extern int
FastMAPeriod = 10; extern int SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int
BánTicket; điểm sử dụng gấp đôi; int UseTrượt trượt; 156 www.ZTCprep.com Phụ lục
A // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol()); UseSlippage =
GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường trung bình
động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,0); // Lệnh mua if(FastMA > SlowMA && BuyTicket ==
0) { OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0
&& SellTicket > 0 && OrderType() == OP_SELL) { double CloseLots = OrderLots(); gấp
đôi Giá đóng cửa = Hỏi; bool Đã đóng =
OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } // Xóa đơn hàng khác
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_SELLSTOP) { bool
Đã xóa = OrderDelete(SellTicket,Red); } double PendingPrice = Cao[0] + (PendingPips *
UsePoint); // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss =
PendingPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit = Đang
chờ xử lý + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Stop Order",MagicNumber,0,Green); BánTicket = 0; }
157 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA // Bán lệnh if(FastMA <
SlowMA && SellTicket == 0) { OrderSelect(BuyTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0 && BuyTicket > 0 && OrderType() == OP_BUY) { CloseLots =
OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } else
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_BUYSTOP) { Đã xóa
= OrderDelete(SellTicket,Red); } Giá đang chờ xử lý = Thấp[0] - (PendingPips *
UsePoint); if(StopLoss > 0) double SellStopLoss = Giá đang chờ xử lý + (StopLoss *
UsePoint); if(TakeProfit > 0) double SellTakeProfit = Đang chờ xử lý - (TakeProfit *
UsePoint); SellTicket =
OrderSend(Symbol(),OP_SELLSTOP,LotSize,PendingPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Lệnh dừng bán",MagicNumber,0,Red); MuaVé = 0; } trả
về(0); } // Hàm điểm Pip nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits =
MarketInfo(Tiền tệ,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 3) gấp đôi
CalcPoint = 0,01; khác nếu(CalcDigits == 4 || CalcDigits == 5) CalcPoint = 0,0001;
return(CalcPoint); } // Lấy hàm trượt giá int GetSlippage(string Money, int
SlippagePips) { int CalcDigits = MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2
|| CalcDigits == 4) double CalcSlippage = SlippagePips; else if(CalcDigits == 3 ||
CalcDigits == 5) CalcSlippage = SlippagePips * 10; return(CalcSlippage); } 158
www.ZTCprep.com Phụ lục A 159 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục B Cố vấn chuyên gia nâng cao Đây là chuyên gia cố vấn với
các tính năng nâng cao từ chương 3. #property Copyright "Andrew Young" #include và
dd là ngày có hai chữ số. Bạn có thể xem nhật ký trong Notepad hoặc bất kỳ trình
soạn thảo văn bản nào. Hãy minh họa một ví dụ về cách bạn có thể sử dụng nhật ký
để xác định vấn đề lập trình. Mã bên dưới có lỗi và nó không hoạt động như chúng tôi
mong đợi. Để có thể chẩn đoán được vấn đề, 141 www.ZTCprep.com CHƯƠNG TRÌNH
TƯ VẤN CHUYÊN NGHIỆP chúng ta cần kiểm tra đầu vào hoặc đầu ra của hàm. Hãy
tạo một câu lệnh Print() và in nội dung của tất cả các biến có liên quan vào nhật ký.
Chúng tôi sẽ chạy EA trong Trình kiểm tra chiến lược, chỉ sử dụng Giá mở làm mô hình
thử nghiệm. Hãy đảm bảo rằng bạn đang thử nghiệm EA trong một khoảng thời gian
đủ dài để EA có thể đặt đủ giao dịch cho chúng tôi phân tích. Nếu bạn cần kiểm tra giá
trên biểu đồ, hãy nhấn nút Mở biểu đồ để mở biểu đồ hiển thị các giao dịch được mô
phỏng. Tiếp theo, chúng ta sẽ chuyển đến tab Nhật ký và kiểm tra thông tin chúng ta
cần. Nếu chúng ta cần xem toàn bộ nhật ký hoặc nếu có giao dịch không hiển thị trong
tab Nhật ký, chúng ta có thể nhấp chuột phải và chọn Mở từ menu bật lên và mở tệp
nhật ký trực tiếp. Mã này đang gây ra lỗi 130: "điểm dừng không hợp lệ" mỗi khi chúng
tôi đặt lệnh mua. Chúng tôi biết rằng lỗi 130 có nghĩa là lệnh dừng lỗ hoặc chốt lãi
không chính xác. Bạn có thể xác định được lỗi không? if(Close[0] > MA && BuyTicket
== 0) { double Openprice = Hỏi; gấp đôi BuyStopLoss = OpenPrice + (StopLoss *
UsePoint); gấp đôi BuyTakeProfit = OpenPrice + (TakeProfit * UsePoint); BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,Openprice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Mua đơn hàng",MagicNumber,0,Green); BánTicket = 0; }
Chúng ta sẽ sử dụng hàm Print() để xác minh các tham số đang được truyền cho hàm
OrderSend(). Chúng ta sẽ tập trung vào giá mở lệnh, mức dừng lỗ và chốt lời.
Print("Giá:"+OpenPrice+" Dừng:"+BuyStopLoss+" Lợi nhuận:"+BuyTakeProfit); Đây là
kết quả khi chúng tôi chạy EA trong trình thử nghiệm chiến lược. Giả sử mức dừng lỗ
và chốt lời là 50 pips: 11:52:12 2009.11.02 02:00 Ví dụ EURUSD,H1: Lỗi gửi lệnh 130
11:52:12 2009.11.02 02:00 Ví dụ EURUSD,H1: Giá:1.47340000 Dừng:1.47840000 Lợi
nhuận:1.47840000 Chúng tôi biết rằng mức dừng lỗ phải thấp hơn giá mở cửa cho
một lệnh mua. Ở đây, nó cao hơn giá. Trên thực tế, nó có cùng mức giá với mức chốt
lời. Xem nhanh mã của chúng tôi và chúng tôi nhận ra rằng chúng tôi đã vô tình chèn
dấu cộng vào phương trình dừng lỗ mua. Đây là mã chính xác: 142 www.ZTCprep.com
Mẹo và thủ thuật nhân đôi BuyStopLoss = OpenPrice - (StopLoss * UsePoint); Nếu bạn
nhận được thông báo lỗi khi cố gắng đặt, đóng hoặc sửa đổi đơn hàng, hãy tập trung
nỗ lực vào vấn đề được chỉ ra bởi thông báo lỗi. Dưới đây là một số thông báo lỗi phổ
biến nhất do lỗi lập trình gây ra: • Lỗi 129: Giá không hợp lệ – Giá mở cửa không hợp
lệ. Đối với các lệnh thị trường, hãy đảm bảo giá Mua hoặc Bán hiện tại đang được
thông qua, tùy theo loại lệnh. Đối với các lệnh đang chờ xử lý, hãy đảm bảo giá cao
hơn hoặc thấp hơn giá hiện tại, theo yêu cầu của loại lệnh. Đồng thời kiểm tra xem giá
lệnh chờ xử lý có quá gần với giá hiện tại hay không (tức là nằm trong mức dừng). • Lỗi
130: Điểm dừng không hợp lệ – Giá dừng lỗ hoặc chốt lãi không chính xác. Kiểm tra
xem giá dừng lỗ và chốt lời được đặt ở trên hay dưới giá hiện tại, tùy thuộc vào loại
lệnh là mua hay bán. Đồng thời kiểm tra xem giá dừng lỗ hoặc chốt lãi không quá gần
với giá hiện tại (tức là nằm trong mức dừng). • Lỗi 131: Khối lượng giao dịch không
hợp lệ – Kích thước lô không chính xác. Đảm bảo rằng kích thước lô không vượt quá
mức tối thiểu hoặc tối đa của nhà môi giới và kích thước lô được chuẩn hóa thành giá
trị bước chính xác (0,1 hoặc 0,01 trên hầu hết các nhà môi giới). Bạn có thể tìm thấy
mô tả của tất cả các thông báo lỗi trong Tham chiếu MQL trong Hằng số tiêu chuẩn –
Mã lỗi. Nếu bạn cần hỗ trợ thêm về lỗi mà bạn đang gặp phải, hãy kiểm tra các diễn
đàn tại MQL4.com. Khắc phục sự cố các lỗi giao dịch không liên tục Trong khi hầu hết
các lỗi nghiêm trọng có thể được tìm thấy đơn giản bằng cách kiểm tra lại, những lỗi
khác sẽ chỉ xảy ra trong quá trình giao dịch theo thời gian thực. Lỗi logic có thể dẫn
đến giao dịch không được đặt chính xác và bạn có thể phải mất một chút nỗ lực để
xác định những lỗi này. Nếu có giao dịch được đặt không chính xác trong quá trình
giao dịch demo hoặc giao dịch trực tiếp, chúng tôi cần nhiều thông tin cần thiết để
khắc phục sự cố. Chúng tôi sẽ thêm một tính năng tùy chọn để ghi nhật ký thông tin
trạng thái và giao dịch theo thời gian thực để chúng tôi có bản ghi về nó khi khắc phục
sự cố giao dịch. Chúng tôi sẽ sử dụng câu lệnh Print() như trước đây nhưng chúng tôi
sẽ ghi lại các giá trị chỉ báo, giá cả – bất kỳ thông tin nào hữu ích trong việc gỡ lỗi.
Chúng tôi cũng sẽ thêm một biến bên ngoài để bật và tắt đăng nhập. // Biến ngoài
extern bool Debug = true; 143 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN
NGHIỆP // Đặt ở gần cuối hàm start() if(Debug == true)
Print(StringConcatenate("Bid:",Bid," Ask:",Ask," MA:" ,MA, " MuaTicket:",BuyTicket,"
SellTicket:",SellTicket)); Đoạn mã trên sẽ ghi lại thông tin về giá và chỉ báo cũng như
nội dung của các biến BuyTicket và SellTicket. Nếu có bất kỳ câu hỏi nào về cách mở
giao dịch hoặc tại sao giao dịch không được mở, nhật ký tại thời điểm cụ thể đó sẽ
hiển thị trạng thái của tất cả các điều kiện giao dịch có liên quan. Bạn có thể bật và tắt
đăng nhập bằng biến ngoài Debug. Câu lệnh debug Print() phải được đặt ở gần cuối
hàm start(), sau tất cả các hàm giao dịch. Nếu bạn đang sử dụng bộ hẹn giờ và/hoặc
tính năng thực thi khi mở thanh, hãy đặt câu lệnh debug Print() bên trong khối bộ hẹn
giờ để nó chỉ chạy khi cần thiết. Nếu không, dòng gỡ lỗi sẽ in ra nhật ký sau mỗi tích
tắc, điều này có thể tạo ra một tệp nhật ký lớn. Sửa lỗi biên dịch Khi bạn biên dịch
Expert Advisor, trình biên dịch sẽ kiểm tra cú pháp đúng và đảm bảo rằng tất cả các
hàm và biến tùy chỉnh đã được khai báo đúng. Nếu bạn bỏ sót nội dung nào đó, trình
biên dịch sẽ dừng và mọi lỗi biên dịch sẽ xuất hiện trong tab Lỗi trong cửa sổ Hộp
công cụ. Khi gặp phải một danh sách dài các lỗi biên dịch, hãy luôn bắt đầu từ lỗi đầu
tiên. Bấm đúp vào lỗi trong danh sách và trình soạn thảo sẽ chuyển trực tiếp đến dòng
có lỗi. Sửa lỗi và biên dịch lại. Đôi khi một lỗi cú pháp đơn giản sẽ dẫn đến một số lỗi
không liên quan, mặc dù chỉ có lỗi đầu tiên là hợp lệ. Đây là danh sách các lỗi biên
dịch thường gặp và cách khắc phục: • Biến không được xác định – Bạn quên khai báo
một biến có kiểu dữ liệu. Nếu đó là biến toàn cục hoặc biến ngoài, hãy khai báo nó ở
đầu tệp. Nếu đó là biến cục bộ, hãy tìm lần xuất hiện đầu tiên và đặt phần khai báo
kiểu dữ liệu trước biến đó. Nếu không, hãy kiểm tra chính tả hoặc kiểu chữ
(hoa/thường) của tên biến. • Biến đã được xác định – Bạn đã khai báo cùng một biến
hai lần. Loại bỏ phần khai báo kiểu dữ liệu khỏi tất cả các khai báo biến trùng lặp. •
Hàm không được xác định – Nếu hàm được đề cập nằm trong tệp bao gồm hoặc thư
viện, hãy đảm bảo rằng lệnh #include hoặc #import được đặt ở đầu tệp và đúng. Nếu
không, hãy kiểm tra chính tả hoặc kiểu chữ của tên hàm và đảm bảo rằng nó tồn tại
trong tệp hiện tại hoặc trong tệp bao gồm hoặc thư viện có liên quan. 144
www.ZTCprep.com Mẹo và thủ thuật • Phép gán bất hợp pháp được sử dụng – Điều
này thường liên quan đến dấu bằng (=). Hãy nhớ rằng một dấu bằng duy nhất dành
cho phép gán biến và hai dấu bằng (==) là toán tử so sánh. Sửa toán tử gán thành toán
tử so sánh thích hợp. • Phép gán dự kiến – Điều này thường liên quan đến toán tử so
sánh "bằng" (==). Bạn đã sử dụng hai dấu bằng thay vì một trong phép gán biến. Sửa
toán tử thành một dấu bằng duy nhất. • Dấu ngoặc đơn bên phải không cân bằng –
Điều này thường xảy ra trong câu lệnh if khi sử dụng dấu ngoặc đơn lồng nhau. Đi đến
dòng được chỉ ra bởi lỗi đầu tiên và chèn dấu ngoặc đơn bên trái vào vị trí thích hợp. •
Dấu ngoặc trái không cân bằng – Đây là một dấu ngoặc đơn khó. Lỗi thường xảy ra ở
cuối dòng chương trình. Về cơ bản bạn đã quên dấu ngoặc đơn ở đâu đó. Kiểm tra kỹ
mã bạn đã chỉnh sửa gần đây và tìm dấu ngoặc đơn bên phải bị thiếu. Bạn có thể phải
nhận xét các dòng mã để xác định vấn đề. • Đếm tham số sai – Bạn có quá ít hoặc
quá nhiều đối số trong một hàm. Kiểm tra kỹ cú pháp hàm trong Tham chiếu MQL và
sửa các đối số. • Cần có dấu chấm phẩy – Có thể bạn đã quên đặt dấu chấm phẩy ở
cuối dòng. Đặt dấu chấm phẩy ở cuối dòng trước. Lưu ý rằng thiếu dấu chấm phẩy
cũng có thể gây ra bất kỳ lỗi nào ở trên, vì vậy hãy nhớ đặt các dấu chấm phẩy đó! 145
www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA Chương 9 Các chỉ báo và tập
lệnh tùy chỉnh Không có cuốn sách nào về MQL sẽ hoàn chỉnh nếu không đề cập đến
các chỉ báo và tập lệnh tùy chỉnh. Các chỉ báo tích hợp trong MetaTrader khá hạn chế,
nhưng may mắn thay MQL cho phép các lập trình viên tạo các chỉ báo của riêng họ.
Nếu bạn đang tìm kiếm một chỉ báo phổ biến không có trong MT4 thì rất có thể ai đó
đã tạo một chỉ báo đó. Chương này sẽ là tổng quan cơ bản về việc tạo chỉ báo tùy
chỉnh. Hầu hết các chỉ báo đều sử dụng các công thức toán học phức tạp và đó là lĩnh
vực của các lập trình viên giàu kinh nghiệm hơn. Tuy nhiên, một chỉ báo không cần
phải phức tạp. Chúng ta sẽ tạo một chỉ báo tùy chỉnh trong chương này chỉ sử dụng
một vài dòng mã. Bộ đệm Bộ đệm là mảng lưu trữ các giá trị chỉ báo và phép tính. Một
chỉ báo tùy chỉnh có thể có tối đa 8 bộ đệm. Bộ đệm sử dụng các chỉ mục, giống như
mảng và có phạm vi từ 0 đến 7. Khi bạn gọi một chỉ báo tùy chỉnh trong chuyên gia tư
vấn bằng cách sử dụng hàm iCustom(), tham số tiếp theo trong hàm là bộ đệm chỉ
báo. Để tìm vùng đệm thích hợp cho dòng chỉ báo, bạn thường kiểm tra mã nguồn, nếu
có. Nếu mã nguồn được định dạng rõ ràng với các tên biến mô tả, bạn sẽ có thể xác
định bộ đệm thích hợp khá dễ dàng. Chúng ta sẽ đề cập đến cách đặt tên thích hợp
cho bộ đệm chỉ báo trong chương này. Tạo chỉ báo tùy chỉnh Hãy xây dựng một chỉ
báo tùy chỉnh bằng cách sử dụng hai chỉ báo MetaTrader tích hợp để tính toán các
đường của chúng tôi. Chúng ta sẽ xây dựng một chỉ báo Dải Bollinger đã được sửa
đổi. Dải Bollinger bao gồm 3 đường – đường trung tâm là đường trung bình động đơn
giản, cùng với đường trên và đường dưới có giá trị được xác định bằng độ lệch chuẩn.
Chúng ta có thể tạo chỉ báo Dải bollinger của riêng mình bằng cách sử dụng các chỉ
báo Trung bình trượt và Độ lệch chuẩn. Chúng tôi muốn tạo một chỉ báo sử dụng
đường trung bình động hàm mũ để tính toán các đường, trái ngược với đường trung
bình động đơn giản. 146 www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Chúng
tôi bắt đầu bằng cách sử dụng trình hướng dẫn để tạo tệp chỉ báo của mình. Chọn Mới
từ menu Tệp hoặc thanh công cụ để mở trình hướng dẫn và tạo chỉ báo tùy chỉnh. Điền
tên chỉ báo và thêm thông số nếu muốn. Trên trang cuối cùng, chúng tôi đã thêm ba
dòng chỉ báo cùng màu. Đây là kết quả của thuật sĩ. Hiện tại chúng ta đã bỏ qua hàm
start(): //+--------------------------------- --------------------------------+ //| EMA Bollinger.mq4 | //|
Andrew trẻ | //| http://www.easyexpertforex.com | //+-----------------------------------------------------
-- -------------------+ #bản quyền tài sản "Andrew Young" #link tài sản
"http://www.easyexpertforex.com" #property Indicator_chart_window #property
Indicator_buffers 3 #property Indicator_color1 DeepSkyBlue #property
Indicator_color2 DeepSkyBlue #property Indicator_color3 DeepSkyBlue //---- đệm gấp
đôi ExtMapBuffer1[]; nhân đôi ExtMapBuffer2[]; nhân đôi ExtMapBuffer3[]; //+--------------
----------------------------------------- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+---
---------------------------------------------------- -------------------+ int init() { //---- chỉ báo
SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,ExtMapBuffer1);
SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,ExtMapBuffer2);
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,ExtMapBuffer3); //---- return(0); } Hãy
chuyển sự chú ý của chúng ta tới các phần tử được in đậm. Khai báo #property đặt
tham số cho bộ đệm chỉ báo của chúng tôi. Thuộc tính Indicator_chart_window vẽ chỉ
báo của chúng tôi trong cửa sổ biểu đồ chính. Nếu chúng ta đang tạo một bộ dao
động và muốn vẽ chỉ báo trong một cửa sổ riêng biệt, thì thay vào đó, chúng ta sẽ sử
dụng thuộc tính Indicator_separate_window. Thuộc tính Indicator_buffers đặt số lượng
bộ đệm cho chỉ báo của chúng tôi. Trong trường hợp này chúng tôi đang sử dụng ba
bộ đệm. Thuộc tính Indicator_color đặt màu của cả ba dòng thành DeepSkyBlue. 147
www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN NGHIỆP Tiếp theo là các khai báo
cho mảng bộ đệm của chúng ta. Chúng tôi có ba bộ đệm có tên ExtMapBuffer(1-3).
Chúng tôi sẽ sớm thay đổi các mã định danh mảng này thành một cái gì đó mang tính
mô tả hơn. Hàm init() là nơi chúng ta đặt các thuộc tính cho bộ đệm chỉ báo của mình.
SetIndexBuffer() liên kết một mảng bộ đệm với một chỉ mục bộ đệm. Chỉ mục bộ đệm
là những gì chúng tôi đề cập đến khi đặt thuộc tính cho một dòng chỉ báo cũng như
khi chúng tôi gọi một dòng chỉ báo từ EA bằng cách sử dụng hàm iCustom(). Tham số
đầu tiên là một số nguyên từ 0 đến 7 và tham số thứ hai là tên của mảng đệm. Các
thuộc tính của bản vẽ Hàm SetIndexStyle() thiết lập loại đường để vẽ, cùng với các
thuộc tính của đường đó. Mỗi dòng chỉ báo sẽ có hàm SetIndexStyle() tương ứng. Đây
là cú pháp: void SetIndexStyle(int BufferIndex, int LineType, int LineStyle = EMPTY, int
LineWidth = EMPTY, color LineColor = CLR_NONE) • BufferIndex – Chỉ mục của bộ
đệm, từ 0 đến 7. • LineType – Đặt loại bộ đệm đường để vẽ. DRAW_LINE vẽ một
đường duy nhất, DRAW_HISTOGRAM vẽ biểu đồ dọc (xem ví dụ về OsMA hoặc Bộ tạo
dao động tuyệt vời), DRAW_ARROW vẽ một biểu tượng và DRAW_NONE không vẽ
đường nào. • LineStyle – Một tham số tùy chọn cho biết kiểu vẽ. Được sử dụng chủ
yếu cho các dòng loại DRAW_LINE. Theo mặc định, một đường liền nét được vẽ
(STYLE_SOLID). Bạn cũng có thể vẽ các đường đứt nét (STYLE_DASH) và chấm
(STYLE_DOT). • LineWidth – Một tham số tùy chọn cho biết chiều rộng của đường tính
bằng pixel. Giá trị mặc định là 1. • LineColor – Một tham số tùy chọn cho biết màu của
đường. Nếu bạn sử dụng trình hướng dẫn, màu sẽ được đặt bằng cách sử dụng khai
báo #property, nhưng bạn cũng có thể đặt màu ở đây. Nếu bạn đang sử dụng
DRAW_ARROW làm LineType, hàm SetArrow() cho phép bạn đặt ký hiệu phông chữ
Wingdings để vẽ trên biểu đồ. Tham số đầu tiên là chỉ mục bộ đệm và tham số thứ hai
là hằng số nguyên biểu thị ký hiệu cần vẽ. Các ký hiệu có thể được tìm thấy trong
Tham chiếu MQL trong Hằng số tiêu chuẩn – Mã mũi tên. Bạn có thể muốn thêm mô
tả cho các dòng chỉ báo sẽ được hiển thị trong chú giải công cụ hoặc trong cửa sổ dữ
liệu. Để thực hiện việc này, hãy sử dụng hàm SetIndexLabel(). Tham số đầu tiên là chỉ
mục bộ đệm và tham số thứ hai là mô tả văn bản. Chúng tôi sẽ sớm thêm những thứ
này vào chỉ báo của chúng tôi. 148 www.ZTCprep.com Các chỉ báo và tập lệnh tùy
chỉnh Nếu chỉ báo của bạn được vẽ trong một cửa sổ riêng biệt (chẳng hạn như bộ
dao động) và bạn muốn thêm các mức để biểu thị mức quá mua hoặc quá bán (chẳng
hạn như trong các chỉ báo Stochastic hoặc RSI) hoặc mức 0 (chẳng hạn như trong chỉ
báo CCI), bạn có thể sử dụng các hàm SetLevelStyle() và SetLevelValue(). Xem Tham
chiếu MQL trong Chỉ báo tùy chỉnh để biết thêm thông tin. Bạn cũng có thể muốn chỉ
định một tên chỉ báo ngắn gọn sẽ được hiển thị ở góc trên cùng bên trái của cửa sổ
chỉ báo. Sử dụng hàm IndicatorShortName() để đặt giá trị này. Tham số duy nhất là
một chuỗi văn bản sẽ xuất hiện ở góc trên bên trái của cửa sổ chỉ báo, cũng như trong
cửa sổ dữ liệu. Sử dụng tên bộ đệm mô tả Đây là mã chỉ báo được cập nhật của chúng
tôi. Lưu ý rằng chúng tôi đã đổi tên các mảng đệm để mang tính mô tả rõ hơn về chức
năng thực tế của chúng. Chúng tôi đã thay đổi tham số thứ hai của hàm
SetIndexBuffer() để phản ánh tên bộ đệm mới. Chúng tôi cũng đã thêm
SetIndexLabel() cho mỗi dòng để hiển thị tên mô tả trong Cửa sổ dữ liệu. //---- đệm đôi
EMA[]; gấp đôi UpperBand[]; gấp đôi LowerBand[]; //+-----------------------------------------------------
-- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+------------------------------------------
------------- -------------------+ int init() { //---- chỉ báo SetIndexStyle(0,DRAW_LINE);
SetIndexBuffer(0,EMA); SetIndexLabel(0,"EMA"); SetIndexStyle(1,DRAW_LINE);
SetIndexBuffer(1,UpperBand); SetIndexLabel(1,"UpperBand");
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,LowerBand);
SetIndexLabel(2,"LowerBand"); //---- return(0); } Chúng tôi đã đổi tên các mảng vùng
đệm từ tên mặc định (ExtMapBuffer) thành tên mang tính mô tả hơn. EMA[] sẽ là vùng
đệm của chúng tôi cho đường trung tâm và UpperBand[] và LowerBand[] sẽ lần lượt là
dải trên và dải dưới. 149 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Các
hàm SetIndexBuffer() liên kết các chỉ mục bộ đệm với mảng bộ đệm của chúng ta.
EMA là 0, UpperBand là 1 và LowerBand là 2. Lưu ý rằng các dấu ngoặc nhọn bị loại
khỏi tên định danh mảng cho tham số SetIndexBuffer() thứ hai. Các hàm
SetIndexLabel() đặt tên mô tả cho từng vùng đệm chỉ báo. Trong trường hợp này, tên
dòng giống với tên định danh của chúng tôi. Chúng sẽ xuất hiện trên chú giải công cụ
chuột cũng như trong Cửa sổ dữ liệu. Nếu một lập trình viên khác quyết định sử dụng
chỉ báo này trong chuyên gia cố vấn, thì định dạng ở trên sẽ làm rõ chính xác chỉ mục
bộ đệm chỉ báo nào họ nên sử dụng cho mỗi dòng. Hàm start() của chỉ báo Trình
hướng dẫn chỉ chèn một biểu thức vào hàm start(): int count_bars =
IndicatorCounted(); IndicatorCounted() trả về số thanh trên biểu đồ mà chỉ báo đã tính
toán. Khi EA được khởi động lần đầu tiên, giá trị này sẽ là 0. Chỉ báo sẽ được tính cho
mọi thanh trên biểu đồ. Trên các thanh tiếp theo, chúng ta sẽ kiểm tra hàm
IndicatorCounted() để xem có bao nhiêu thanh đã được tính toán, từ đó chúng ta sẽ
biết chính xác có bao nhiêu thanh mới cần tính toán. Việc tính toán chỉ báo của chúng
tôi sẽ diễn ra bên trong vòng lặp for. Điểm bắt đầu sẽ là thanh chưa được tính toán
đầu tiên và điểm kết thúc sẽ là thanh hiện tại. Chúng ta sẽ so sánh giá trị của
IndicatorCounted() với biến Bars được xác định trước, biến này trả về số lượng thanh
trên biểu đồ hiện tại. Điều này sẽ xác định điểm khởi đầu của chúng tôi. Đây là mã cho
vòng lặp for: int count_bars = IndicatorCounted(); if(counted_bars > 0) count_bars--; int
CalculateBars = Thanh - count_bars; for(int Count = CalculateBars; Count >= 0; Count-
-) { // Tính toán chỉ báo } Câu lệnh if đầu tiên sẽ giảm giá trị của count_bars đi 1 khi
tính toán các thanh mới. Chúng tôi sẽ luôn tính toán ít nhất hai thanh trước đó. Điều
này là do điều kiện có thể không tính được dấu tích cuối cùng của thanh trong một số
trường hợp. Tiếp theo, chúng tôi xác định số lượng thanh cần tính toán bằng cách trừ
count_bars khỏi biến Bars được xác định trước. Điều này được lưu trữ trong biến
CalculateBars. 150 www.ZTCprep.com Chỉ báo và Tập lệnh Tùy chỉnh Trong vòng lặp
for của chúng tôi, biến tăng Count được đặt thành giá trị của CalculateBars, điều kiện
để kết thúc là khi Count nhỏ hơn 0 và biến Count được giảm dần sau mỗi lần lặp. Điều
này sẽ tính toán từng thanh trên biểu đồ từ trái sang phải. Đây là mã để tính Dải
Bollinger của chúng tôi. Chúng ta sẽ khai báo biến ngoài BandsPeriod ở đầu tệp. Vòng
lặp for là vòng lặp chúng ta đã tạo ở trên: // Các tham số bên ngoài extern int
BandsPeriod = 20; // hàm start() for(int Count = CalculateBars; Count >= 0; Count--) {
EMA[Count] = iMA(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); UpperBand[Count] = EMA[Count]
+ StdDev; LowerBand[Count] = EMA[Count] - StdDev; } Đầu tiên, chúng ta gọi chỉ báo
Trung bình trượt tích hợp bằng hàm iMA() và gán giá trị trả về cho EMA[Count]. Lưu ý
rằng chỉ mục mảng và tham số Shift cho chỉ báo trung bình động đều sử dụng giá trị
Đếm hiện tại. Tiếp theo, chúng tôi gọi chỉ báo Độ lệch chuẩn bằng iStdDev(). Để tính
dải trên, tất cả những gì chúng ta cần làm là cộng độ lệch chuẩn vào đường trung bình
động. Điều này được lưu trữ trong mảng đệm UpperBand[]. Để tính LowerBand[],
chúng tôi trừ đi độ lệch chuẩn khỏi mức trung bình động. Hãy mở rộng chỉ báo của
chúng tôi thêm một chút bằng cách cung cấp cho nó đầy đủ các cài đặt. Chúng tôi sẽ
thêm cài đặt để điều chỉnh dịch chuyển kỳ hạn, phương pháp trung bình động và các
tham số giá được áp dụng cũng như điều chỉnh độ lệch chuẩn: // Tham số bên ngoài
extern int BandsPeriod = 20; extern int BandsShift = 0; extern int BandsMethod = 1;
extern int BandsPrice = 0; extern int Độ lệch = 1; 151 www.ZTCprep.com LẬP TRÌNH
TƯ VẤN CHUYÊN NGHIỆP // start() function for(int Count = CalculateBars; Count >= 0;
Count--) { EMA[Count] =
iMA(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice ,Đếm); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice,Count);
UpperBand[Count] = EMA[Count] + (StdDev * Độ lệch); LowerBand[Count] =
EMA[Count] - (StdDev * Độ lệch); } Chúng tôi đã thêm các biến bên ngoài để điều chỉnh
các tham số còn lại cho hàm iMA() và iStdDev(). Chúng tôi cũng đã thêm một tham số
để điều chỉnh số lượng độ lệch chuẩn. Để tính toán điều này, chúng tôi chỉ cần nhân
StdDev với Độ lệch. Bây giờ chúng ta có chỉ báo Dải bollinger có thể điều chỉnh hoàn
toàn, linh hoạt hơn chỉ báo MetaTrader tiêu chuẩn. Mã đầy đủ được liệt kê trong Phụ
lục E. Bạn có thể làm được nhiều việc hơn với các chỉ báo tùy chỉnh thay vì chỉ tính
toán lại các chỉ báo tích hợp. Tùy thuộc vào trình độ kiến thức toán học của bạn, bạn
có thể mã hóa các chỉ báo không có trong MetaTrader hoặc thậm chí tạo chỉ báo của
riêng bạn. Bạn cũng có thể vẽ và thao tác các đối tượng. Nếu bạn muốn tìm hiểu thêm
về cách tạo chỉ báo tùy chỉnh, hãy xem chủ đề Tham khảo MQL Chỉ báo tùy chỉnh,
Hàm đối tượng và Toán & Lượng giác. Tập lệnh Tập lệnh là một chương trình MQL chỉ
chạy một lần khi nó được đính kèm lần đầu tiên vào biểu đồ. Tập lệnh có thể được sử
dụng để tự động hóa một loạt hành động giao dịch, chẳng hạn như đóng tất cả các
lệnh trên biểu đồ hoặc gửi lệnh chờ xử lý. Một số tập lệnh, chẳng hạn như tập lệnh
Period_converter đi kèm với MetaTrader, có thể vẽ lại biểu đồ dựa trên khoảng thời
gian tùy chỉnh. Tệp mã nguồn tập lệnh phải có chỉ thị thuộc tính show_confirm hoặc
show_inputs. Thuộc tính show_confirm nhắc người dùng xác nhận hoạt động của tập
lệnh, trong khi show_inputs hiển thị hộp thoại thuộc tính tập lệnh. #property
show_confirm // hiển thị hộp thoại xác nhận #property show_inputs // hiển thị hộp
thoại thuộc tính Nếu tập lệnh của bạn có các tham số cần được điều chỉnh, hãy sử
dụng thuộc tính show_inputs. Nếu không, hãy sử dụng show_confirm. 152
www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Cũng giống như các chỉ báo và
cố vấn chuyên gia, các tập lệnh sử dụng các hàm init(), deinit() và start(). Hãy nhớ
rằng mỗi hàm sẽ chỉ được chạy một lần – init() và start() khi tập lệnh được khởi động
và deinit() khi tập lệnh bị xóa. Bạn có thể đính kèm một tập lệnh vào biểu đồ tại một
thời điểm. MetaTrader đi kèm với một số tập lệnh mẫu. Tất cả các tập lệnh được lưu
trong thư mục \experts\scripts. 153 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục A Cố vấn chuyên gia đơn giản Đây là chuyên gia cố vấn đơn
giản từ chương 2. #property bản quyền "Andrew Young" // Biến ngoài extern double
LotSize = 0.1; StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern
int Trượt = 5; extern int MagicNumber = 123; extern int FastMAPeriod = 10; extern int
SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int BánTicket; điểm sử dụng gấp
đôi; int UseTrượt trượt; // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol());
UseSlippage = GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường
trung bình động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi
SlowMA = iMA(NULL,0,SlowMAPeriod,0,0,0,0); 154 www.ZTCprep.com Phụ lục A //
Lệnh mua if(FastMA > SlowMA && BuyTicket == 0) {
OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0 &&
SellTicket > 0) { double CloseLots = OrderLots(); gấp đôi Giá đóng cửa = Hỏi; bool Đã
đóng = OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } double
OpenPrice = Hỏi; // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss
= OpenPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit =
OpenPrice + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Order",MagicNumber,0,Green); BánTicket = 0; } //
Lệnh bán if(FastMA < SlowMA && SellTicket == 0) {
OrderSelect(BuyTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0 && BuyTicket >
0) { CloseLots = OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } OpenPrice = Giá thầu;
if(StopLoss > 0) double SellStopLoss = OpenPrice + (StopLoss * UsePoint);
if(TakeProfit > 0) double SellTakeProfit = OpenPrice - (TakeProfit * UsePoint);
SellTicket = OrderSend(Symbol(),OP_SELL,LotSize,OpenPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Sell Order",MagicNumber,0,Red); MuaVé = 0; } 155
www.ZTCprep.com CHUYÊN GIA LẬP TRÌNH TƯ VẤN return(0); } // Hàm điểm Pip
nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits = MarketInfo(Tiền tệ,MODE_DIGITS);
if(CalcDigits == 2 || CalcDigits == 3) gấp đôi CalcPoint = 0,01; khác nếu(CalcDigits == 4
|| CalcDigits == 5) CalcPoint = 0,0001; return(CalcPoint); } // Lấy hàm trượt giá int
GetSlippage(string Money, int SlippagePips) { int CalcDigits =
MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 4) double
CalcSlippage = SlippagePips; else if(CalcDigits == 3 || CalcDigits == 5) CalcSlippage =
SlippagePips * 10; return(CalcSlippage); } Chuyên gia cố vấn đơn giản với các lệnh
đang chờ xử lý Đây là chuyên gia cố vấn đơn giản sử dụng các lệnh dừng đang chờ xử
lý: #property Copyright "Andrew Young" // Biến bên ngoài extern double LotSize = 0.1;
StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern int
PendingPips = 10; extern int Trượt = 5; extern int MagicNumber = 123; extern int
FastMAPeriod = 10; extern int SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int
BánTicket; điểm sử dụng gấp đôi; int UseTrượt trượt; 156 www.ZTCprep.com Phụ lục
A // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol()); UseSlippage =
GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường trung bình
động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,0); // Lệnh mua if(FastMA > SlowMA && BuyTicket ==
0) { OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0
&& SellTicket > 0 && OrderType() == OP_SELL) { double CloseLots = OrderLots(); gấp
đôi Giá đóng cửa = Hỏi; bool Đã đóng =
OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } // Xóa đơn hàng khác
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_SELLSTOP) { bool
Đã xóa = OrderDelete(SellTicket,Red); } double PendingPrice = Cao[0] + (PendingPips *
UsePoint); // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss =
PendingPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit = Đang
chờ xử lý + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Stop Order",MagicNumber,0,Green); BánTicket = 0; }
157 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA // Bán lệnh if(FastMA <
SlowMA && SellTicket == 0) { OrderSelect(BuyTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0 && BuyTicket > 0 && OrderType() == OP_BUY) { CloseLots =
OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } else
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_BUYSTOP) { Đã xóa
= OrderDelete(SellTicket,Red); } Giá đang chờ xử lý = Thấp[0] - (PendingPips *
UsePoint); if(StopLoss > 0) double SellStopLoss = Giá đang chờ xử lý + (StopLoss *
UsePoint); if(TakeProfit > 0) double SellTakeProfit = Đang chờ xử lý - (TakeProfit *
UsePoint); SellTicket =
OrderSend(Symbol(),OP_SELLSTOP,LotSize,PendingPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Lệnh dừng bán",MagicNumber,0,Red); MuaVé = 0; } trả
về(0); } // Hàm điểm Pip nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits =
MarketInfo(Tiền tệ,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 3) gấp đôi
CalcPoint = 0,01; khác nếu(CalcDigits == 4 || CalcDigits == 5) CalcPoint = 0,0001;
return(CalcPoint); } // Lấy hàm trượt giá int GetSlippage(string Money, int
SlippagePips) { int CalcDigits = MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2
|| CalcDigits == 4) double CalcSlippage = SlippagePips; else if(CalcDigits == 3 ||
CalcDigits == 5) CalcSlippage = SlippagePips * 10; return(CalcSlippage); } 158
www.ZTCprep.com Phụ lục A 159 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục B Cố vấn chuyên gia nâng cao Đây là chuyên gia cố vấn với
các tính năng nâng cao từ chương 3. #property Copyright "Andrew Young" #include
Hãy tạo một câu lệnh Print() và in nội dung của tất cả các biến có liên quan vào nhật
ký. Chúng tôi sẽ chạy EA trong Trình kiểm tra chiến lược, chỉ sử dụng Giá mở làm mô
hình thử nghiệm. Hãy đảm bảo rằng bạn đang thử nghiệm EA trong một khoảng thời
gian đủ dài để EA có thể đặt đủ giao dịch cho chúng tôi phân tích. Nếu bạn cần kiểm
tra giá trên biểu đồ, hãy nhấn nút Mở biểu đồ để mở biểu đồ hiển thị các giao dịch
được mô phỏng. Tiếp theo, chúng ta sẽ chuyển đến tab Nhật ký và kiểm tra thông tin
chúng ta cần. Nếu chúng ta cần xem toàn bộ nhật ký hoặc nếu có giao dịch không
hiển thị trong tab Nhật ký, chúng ta có thể nhấp chuột phải và chọn Mở từ menu bật
lên và mở tệp nhật ký trực tiếp. Mã này đang gây ra lỗi 130: "điểm dừng không hợp lệ"
mỗi khi chúng tôi đặt lệnh mua. Chúng tôi biết rằng lỗi 130 có nghĩa là lệnh dừng lỗ
hoặc chốt lãi không chính xác. Bạn có thể xác định được lỗi không? if(Close[0] > MA
&& BuyTicket == 0) { double Openprice = Hỏi; gấp đôi BuyStopLoss = OpenPrice +
(StopLoss * UsePoint); gấp đôi BuyTakeProfit = OpenPrice + (TakeProfit * UsePoint);
BuyTicket = OrderSend(Symbol(),OP_BUY,LotSize,Openprice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Mua đơn hàng",MagicNumber,0,Green); BánTicket = 0; }
Chúng ta sẽ sử dụng hàm Print() để xác minh các tham số đang được truyền cho hàm
OrderSend(). Chúng ta sẽ tập trung vào giá mở lệnh, mức dừng lỗ và chốt lời.
Print("Giá:"+OpenPrice+" Dừng:"+BuyStopLoss+" Lợi nhuận:"+BuyTakeProfit); Đây là
kết quả khi chúng tôi chạy EA trong trình thử nghiệm chiến lược. Giả sử mức dừng lỗ
và chốt lời là 50 pips: 11:52:12 2009.11.02 02:00 Ví dụ EURUSD,H1: Lỗi gửi lệnh 130
11:52:12 2009.11.02 02:00 Ví dụ EURUSD,H1: Giá:1.47340000 Dừng:1.47840000 Lợi
nhuận:1.47840000 Chúng tôi biết rằng mức dừng lỗ phải thấp hơn giá mở cửa cho
một lệnh mua. Ở đây, nó cao hơn giá. Trên thực tế, nó có cùng mức giá với mức chốt
lời. Xem nhanh mã của chúng tôi và chúng tôi nhận ra rằng chúng tôi đã vô tình chèn
dấu cộng vào phương trình dừng lỗ mua. Đây là mã chính xác: 142 www.ZTCprep.com
Mẹo và thủ thuật nhân đôi BuyStopLoss = OpenPrice - (StopLoss * UsePoint); Nếu bạn
nhận được thông báo lỗi khi cố gắng đặt, đóng hoặc sửa đổi đơn hàng, hãy tập trung
nỗ lực vào vấn đề được chỉ ra bởi thông báo lỗi. Dưới đây là một số thông báo lỗi phổ
biến nhất do lỗi lập trình gây ra: • Lỗi 129: Giá không hợp lệ – Giá mở cửa không hợp
lệ. Đối với các lệnh thị trường, hãy đảm bảo giá Mua hoặc Bán hiện tại đang được
thông qua, tùy theo loại lệnh. Đối với các lệnh đang chờ xử lý, hãy đảm bảo giá cao
hơn hoặc thấp hơn giá hiện tại, theo yêu cầu của loại lệnh. Đồng thời kiểm tra xem giá
lệnh chờ xử lý có quá gần với giá hiện tại hay không (tức là nằm trong mức dừng). • Lỗi
130: Điểm dừng không hợp lệ – Giá dừng lỗ hoặc chốt lãi không chính xác. Kiểm tra
xem giá dừng lỗ và chốt lời được đặt ở trên hay dưới giá hiện tại, tùy thuộc vào loại
lệnh là mua hay bán. Đồng thời kiểm tra xem giá dừng lỗ hoặc chốt lãi không quá gần
với giá hiện tại (tức là nằm trong mức dừng). • Lỗi 131: Khối lượng giao dịch không
hợp lệ – Kích thước lô không chính xác. Đảm bảo rằng kích thước lô không vượt quá
mức tối thiểu hoặc tối đa của nhà môi giới và kích thước lô được chuẩn hóa thành giá
trị bước chính xác (0,1 hoặc 0,01 trên hầu hết các nhà môi giới). Bạn có thể tìm thấy
mô tả của tất cả các thông báo lỗi trong Tham chiếu MQL trong Hằng số tiêu chuẩn –
Mã lỗi. Nếu bạn cần hỗ trợ thêm về lỗi mà bạn đang gặp phải, hãy kiểm tra các diễn
đàn tại MQL4.com. Khắc phục sự cố các lỗi giao dịch không liên tục Trong khi hầu hết
các lỗi nghiêm trọng có thể được tìm thấy đơn giản bằng cách kiểm tra lại, những lỗi
khác sẽ chỉ xảy ra trong quá trình giao dịch theo thời gian thực. Lỗi logic có thể dẫn
đến giao dịch không được đặt chính xác và bạn có thể phải mất một chút nỗ lực để
xác định những lỗi này. Nếu có giao dịch được đặt không chính xác trong quá trình
giao dịch demo hoặc giao dịch trực tiếp, chúng tôi cần nhiều thông tin cần thiết để
khắc phục sự cố. Chúng tôi sẽ thêm một tính năng tùy chọn để ghi nhật ký thông tin
trạng thái và giao dịch theo thời gian thực để chúng tôi có bản ghi về nó khi khắc phục
sự cố giao dịch. Chúng tôi sẽ sử dụng câu lệnh Print() như trước đây nhưng chúng tôi
sẽ ghi lại các giá trị chỉ báo, giá cả – bất kỳ thông tin nào hữu ích trong việc gỡ lỗi.
Chúng tôi cũng sẽ thêm một biến bên ngoài để bật và tắt đăng nhập. // Biến ngoài
extern bool Debug = true; 143 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN
NGHIỆP // Đặt ở gần cuối hàm start() if(Debug == true)
Print(StringConcatenate("Bid:",Bid," Ask:",Ask," MA:" ,MA, " MuaTicket:",BuyTicket,"
SellTicket:",SellTicket)); Đoạn mã trên sẽ ghi lại thông tin về giá và chỉ báo cũng như
nội dung của các biến BuyTicket và SellTicket. Nếu có bất kỳ câu hỏi nào về cách mở
giao dịch hoặc tại sao giao dịch không được mở, nhật ký tại thời điểm cụ thể đó sẽ
hiển thị trạng thái của tất cả các điều kiện giao dịch có liên quan. Bạn có thể bật và tắt
đăng nhập bằng biến ngoài Debug. Câu lệnh debug Print() phải được đặt ở gần cuối
hàm start(), sau tất cả các hàm giao dịch. Nếu bạn đang sử dụng bộ hẹn giờ và/hoặc
tính năng thực thi khi mở thanh, hãy đặt câu lệnh debug Print() bên trong khối bộ hẹn
giờ để nó chỉ chạy khi cần thiết. Nếu không, dòng gỡ lỗi sẽ in ra nhật ký sau mỗi tích
tắc, điều này có thể tạo ra một tệp nhật ký lớn. Sửa lỗi biên dịch Khi bạn biên dịch
Expert Advisor, trình biên dịch sẽ kiểm tra cú pháp đúng và đảm bảo rằng tất cả các
hàm và biến tùy chỉnh đã được khai báo đúng. Nếu bạn bỏ sót nội dung nào đó, trình
biên dịch sẽ dừng và mọi lỗi biên dịch sẽ xuất hiện trong tab Lỗi trong cửa sổ Hộp
công cụ. Khi gặp phải một danh sách dài các lỗi biên dịch, hãy luôn bắt đầu từ lỗi đầu
tiên. Bấm đúp vào lỗi trong danh sách và trình soạn thảo sẽ chuyển trực tiếp đến dòng
có lỗi. Sửa lỗi và biên dịch lại. Đôi khi một lỗi cú pháp đơn giản sẽ dẫn đến một số lỗi
không liên quan, mặc dù chỉ có lỗi đầu tiên là hợp lệ. Đây là danh sách các lỗi biên
dịch thường gặp và cách khắc phục: • Biến không được xác định – Bạn quên khai báo
một biến có kiểu dữ liệu. Nếu đó là biến toàn cục hoặc biến ngoài, hãy khai báo nó ở
đầu tệp. Nếu đó là biến cục bộ, hãy tìm lần xuất hiện đầu tiên và đặt phần khai báo
kiểu dữ liệu trước biến đó. Nếu không, hãy kiểm tra chính tả hoặc kiểu chữ
(hoa/thường) của tên biến. • Biến đã được xác định – Bạn đã khai báo cùng một biến
hai lần. Loại bỏ phần khai báo kiểu dữ liệu khỏi tất cả các khai báo biến trùng lặp. •
Hàm không được xác định – Nếu hàm được đề cập nằm trong tệp bao gồm hoặc thư
viện, hãy đảm bảo rằng lệnh #include hoặc #import được đặt ở đầu tệp và đúng. Nếu
không, hãy kiểm tra chính tả hoặc kiểu chữ của tên hàm và đảm bảo rằng nó tồn tại
trong tệp hiện tại hoặc trong tệp bao gồm hoặc thư viện có liên quan. 144
www.ZTCprep.com Mẹo và thủ thuật • Phép gán bất hợp pháp được sử dụng – Điều
này thường liên quan đến dấu bằng (=). Hãy nhớ rằng một dấu bằng duy nhất dành
cho phép gán biến và hai dấu bằng (==) là toán tử so sánh. Sửa toán tử gán thành toán
tử so sánh thích hợp. • Phép gán dự kiến – Điều này thường liên quan đến toán tử so
sánh "bằng" (==). Bạn đã sử dụng hai dấu bằng thay vì một trong phép gán biến. Sửa
toán tử thành một dấu bằng duy nhất. • Dấu ngoặc đơn bên phải không cân bằng –
Điều này thường xảy ra trong câu lệnh if khi sử dụng dấu ngoặc đơn lồng nhau. Đi đến
dòng được chỉ ra bởi lỗi đầu tiên và chèn dấu ngoặc đơn bên trái vào vị trí thích hợp. •
Dấu ngoặc trái không cân bằng – Đây là một dấu ngoặc đơn khó. Lỗi thường xảy ra ở
cuối dòng chương trình. Về cơ bản bạn đã quên dấu ngoặc đơn ở đâu đó. Kiểm tra kỹ
mã bạn đã chỉnh sửa gần đây và tìm dấu ngoặc đơn bên phải bị thiếu. Bạn có thể phải
nhận xét các dòng mã để xác định vấn đề. • Đếm tham số sai – Bạn có quá ít hoặc
quá nhiều đối số trong một hàm. Kiểm tra kỹ cú pháp hàm trong Tham chiếu MQL và
sửa các đối số. • Cần có dấu chấm phẩy – Có thể bạn đã quên đặt dấu chấm phẩy ở
cuối dòng. Đặt dấu chấm phẩy ở cuối dòng trước. Lưu ý rằng thiếu dấu chấm phẩy
cũng có thể gây ra bất kỳ lỗi nào ở trên, vì vậy hãy nhớ đặt các dấu chấm phẩy đó! 145
www.ZTCprep.com CHƯƠNG TRÌNH CỐ VẤN CHUYÊN GIA Chương 9 Các chỉ báo và
tập lệnh tùy chỉnh Không có cuốn sách nào về MQL sẽ hoàn chỉnh nếu không đề cập
đến các chỉ báo và tập lệnh tùy chỉnh. Các chỉ báo tích hợp trong MetaTrader khá hạn
chế, nhưng may mắn thay MQL cho phép các lập trình viên tạo các chỉ báo của riêng
họ. Nếu bạn đang tìm kiếm một chỉ báo phổ biến không có trong MT4 thì rất có thể ai
đó đã tạo một chỉ báo đó. Chương này sẽ là tổng quan cơ bản về việc tạo chỉ báo tùy
chỉnh. Hầu hết các chỉ báo đều sử dụng các công thức toán học phức tạp và đó là lĩnh
vực của các lập trình viên giàu kinh nghiệm hơn. Tuy nhiên, một chỉ báo không cần
phải phức tạp. Chúng ta sẽ tạo một chỉ báo tùy chỉnh trong chương này chỉ sử dụng
một vài dòng mã. Bộ đệm Bộ đệm là mảng lưu trữ các giá trị chỉ báo và phép tính. Một
chỉ báo tùy chỉnh có thể có tối đa 8 bộ đệm. Bộ đệm sử dụng các chỉ mục, giống như
mảng và có phạm vi từ 0 đến 7. Khi bạn gọi một chỉ báo tùy chỉnh trong chuyên gia tư
vấn bằng cách sử dụng hàm iCustom(), tham số tiếp theo trong hàm là bộ đệm chỉ
báo. Để tìm vùng đệm thích hợp cho dòng chỉ báo, bạn thường kiểm tra mã nguồn, nếu
có. Nếu mã nguồn được định dạng rõ ràng với các tên biến mô tả, bạn sẽ có thể xác
định bộ đệm thích hợp khá dễ dàng. Chúng ta sẽ đề cập đến cách đặt tên thích hợp
cho bộ đệm chỉ báo trong chương này. Tạo chỉ báo tùy chỉnh Hãy xây dựng một chỉ
báo tùy chỉnh bằng cách sử dụng hai chỉ báo MetaTrader tích hợp để tính toán các
đường của chúng tôi. Chúng ta sẽ xây dựng một chỉ báo Dải Bollinger đã được sửa
đổi. Dải Bollinger bao gồm 3 đường – đường trung tâm là đường trung bình động đơn
giản, cùng với đường trên và đường dưới có giá trị được xác định bằng độ lệch chuẩn.
Chúng ta có thể tạo chỉ báo Dải bollinger của riêng mình bằng cách sử dụng các chỉ
báo Trung bình trượt và Độ lệch chuẩn. Chúng tôi muốn tạo một chỉ báo sử dụng
đường trung bình động hàm mũ để tính toán các đường, trái ngược với đường trung
bình động đơn giản. 146 www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Chúng
tôi bắt đầu bằng cách sử dụng trình hướng dẫn để tạo tệp chỉ báo của mình. Chọn Mới
từ menu Tệp hoặc thanh công cụ để mở trình hướng dẫn và tạo chỉ báo tùy chỉnh. Điền
tên chỉ báo và thêm thông số nếu muốn. Trên trang cuối cùng, chúng tôi đã thêm ba
dòng chỉ báo cùng màu. Đây là kết quả của thuật sĩ. Hiện tại chúng ta đã bỏ qua hàm
start(): //+--------------------------------- --------------------------------+ //| EMA Bollinger.mq4 | //|
Andrew trẻ | //| http://www.easyexpertforex.com | //+-----------------------------------------------------
-- -------------------+ #bản quyền tài sản "Andrew Young" #link tài sản
"http://www.easyexpertforex.com" #property Indicator_chart_window #property
Indicator_buffers 3 #property Indicator_color1 DeepSkyBlue #property
Indicator_color2 DeepSkyBlue #property Indicator_color3 DeepSkyBlue //---- đệm gấp
đôi ExtMapBuffer1[]; nhân đôi ExtMapBuffer2[]; nhân đôi ExtMapBuffer3[]; //+--------------
----------------------------------------- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+---
---------------------------------------------------- -------------------+ int init() { //---- chỉ báo
SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,ExtMapBuffer1);
SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,ExtMapBuffer2);
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,ExtMapBuffer3); //---- return(0); } Hãy
chuyển sự chú ý của chúng ta tới các phần tử được in đậm. Khai báo #property đặt
tham số cho bộ đệm chỉ báo của chúng tôi. Thuộc tính Indicator_chart_window vẽ chỉ
báo của chúng tôi trong cửa sổ biểu đồ chính. Nếu chúng ta đang tạo một bộ dao
động và muốn vẽ chỉ báo trong một cửa sổ riêng biệt, thì thay vào đó, chúng ta sẽ sử
dụng thuộc tính Indicator_separate_window. Thuộc tính Indicator_buffers đặt số lượng
bộ đệm cho chỉ báo của chúng tôi. Trong trường hợp này chúng tôi đang sử dụng ba
bộ đệm. Thuộc tính Indicator_color đặt màu của cả ba dòng thành DeepSkyBlue. 147
www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN NGHIỆP Tiếp theo là các khai báo
cho mảng bộ đệm của chúng ta. Chúng tôi có ba bộ đệm có tên ExtMapBuffer(1-3).
Chúng tôi sẽ sớm thay đổi các mã định danh mảng này thành một cái gì đó mang tính
mô tả hơn. Hàm init() là nơi chúng ta đặt các thuộc tính cho bộ đệm chỉ báo của mình.
SetIndexBuffer() liên kết một mảng bộ đệm với một chỉ mục bộ đệm. Chỉ mục bộ đệm
là những gì chúng tôi đề cập đến khi đặt thuộc tính cho một dòng chỉ báo cũng như
khi chúng tôi gọi một dòng chỉ báo từ EA bằng cách sử dụng hàm iCustom(). Tham số
đầu tiên là một số nguyên từ 0 đến 7 và tham số thứ hai là tên của mảng đệm. Các
thuộc tính của bản vẽ Hàm SetIndexStyle() thiết lập loại đường để vẽ, cùng với các
thuộc tính của đường đó. Mỗi dòng chỉ báo sẽ có hàm SetIndexStyle() tương ứng. Đây
là cú pháp: void SetIndexStyle(int BufferIndex, int LineType, int LineStyle = EMPTY, int
LineWidth = EMPTY, color LineColor = CLR_NONE) • BufferIndex – Chỉ mục của bộ
đệm, từ 0 đến 7. • LineType – Đặt loại bộ đệm đường để vẽ. DRAW_LINE vẽ một
đường duy nhất, DRAW_HISTOGRAM vẽ biểu đồ dọc (xem ví dụ về OsMA hoặc Bộ tạo
dao động tuyệt vời), DRAW_ARROW vẽ một biểu tượng và DRAW_NONE không vẽ
đường nào. • LineStyle – Một tham số tùy chọn cho biết kiểu vẽ. Được sử dụng chủ
yếu cho các dòng loại DRAW_LINE. Theo mặc định, một đường liền nét được vẽ
(STYLE_SOLID). Bạn cũng có thể vẽ các đường đứt nét (STYLE_DASH) và chấm
(STYLE_DOT). • LineWidth – Một tham số tùy chọn cho biết chiều rộng của đường tính
bằng pixel. Giá trị mặc định là 1. • LineColor – Một tham số tùy chọn cho biết màu của
đường. Nếu bạn sử dụng trình hướng dẫn, màu sẽ được đặt bằng cách sử dụng khai
báo #property, nhưng bạn cũng có thể đặt màu ở đây. Nếu bạn đang sử dụng
DRAW_ARROW làm LineType, hàm SetArrow() cho phép bạn đặt ký hiệu phông chữ
Wingdings để vẽ trên biểu đồ. Tham số đầu tiên là chỉ mục bộ đệm và tham số thứ hai
là hằng số nguyên biểu thị ký hiệu cần vẽ. Các ký hiệu có thể được tìm thấy trong
Tham chiếu MQL trong Hằng số tiêu chuẩn – Mã mũi tên. Bạn có thể muốn thêm mô
tả cho các dòng chỉ báo sẽ được hiển thị trong chú giải công cụ hoặc trong cửa sổ dữ
liệu. Để thực hiện việc này, hãy sử dụng hàm SetIndexLabel(). Tham số đầu tiên là chỉ
mục bộ đệm và tham số thứ hai là mô tả văn bản. Chúng tôi sẽ sớm thêm những thứ
này vào chỉ báo của chúng tôi. 148 www.ZTCprep.com Các chỉ báo và tập lệnh tùy
chỉnh Nếu chỉ báo của bạn được vẽ trong một cửa sổ riêng biệt (chẳng hạn như bộ
dao động) và bạn muốn thêm các mức để biểu thị mức quá mua hoặc quá bán (chẳng
hạn như trong các chỉ báo Stochastic hoặc RSI) hoặc mức 0 (chẳng hạn như trong chỉ
báo CCI), bạn có thể sử dụng các hàm SetLevelStyle() và SetLevelValue(). Xem Tham
chiếu MQL trong Chỉ báo tùy chỉnh để biết thêm thông tin. Bạn cũng có thể muốn chỉ
định một tên chỉ báo ngắn gọn sẽ được hiển thị ở góc trên cùng bên trái của cửa sổ
chỉ báo. Sử dụng hàm IndicatorShortName() để đặt giá trị này. Tham số duy nhất là
một chuỗi văn bản sẽ xuất hiện ở góc trên bên trái của cửa sổ chỉ báo, cũng như trong
cửa sổ dữ liệu. Sử dụng tên bộ đệm mô tả Đây là mã chỉ báo được cập nhật của chúng
tôi. Lưu ý rằng chúng tôi đã đổi tên các mảng đệm để mang tính mô tả rõ hơn về chức
năng thực tế của chúng. Chúng tôi đã thay đổi tham số thứ hai của hàm
SetIndexBuffer() để phản ánh tên bộ đệm mới. Chúng tôi cũng đã thêm
SetIndexLabel() cho mỗi dòng để hiển thị tên mô tả trong Cửa sổ dữ liệu. //---- đệm đôi
EMA[]; gấp đôi UpperBand[]; gấp đôi LowerBand[]; //+-----------------------------------------------------
-- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+------------------------------------------
------------- -------------------+ int init() { //---- chỉ báo SetIndexStyle(0,DRAW_LINE);
SetIndexBuffer(0,EMA); SetIndexLabel(0,"EMA"); SetIndexStyle(1,DRAW_LINE);
SetIndexBuffer(1,UpperBand); SetIndexLabel(1,"UpperBand");
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,LowerBand);
SetIndexLabel(2,"LowerBand"); //---- return(0); } Chúng tôi đã đổi tên các mảng vùng
đệm từ tên mặc định (ExtMapBuffer) thành tên mang tính mô tả hơn. EMA[] sẽ là vùng
đệm của chúng tôi cho đường trung tâm và UpperBand[] và LowerBand[] sẽ lần lượt là
dải trên và dải dưới. 149 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Các
hàm SetIndexBuffer() liên kết các chỉ mục bộ đệm với mảng bộ đệm của chúng ta.
EMA là 0, UpperBand là 1 và LowerBand là 2. Lưu ý rằng các dấu ngoặc nhọn bị loại
khỏi tên định danh mảng cho tham số SetIndexBuffer() thứ hai. Các hàm
SetIndexLabel() đặt tên mô tả cho từng vùng đệm chỉ báo. Trong trường hợp này, tên
dòng giống với tên định danh của chúng tôi. Chúng sẽ xuất hiện trên chú giải công cụ
chuột cũng như trong Cửa sổ dữ liệu. Nếu một lập trình viên khác quyết định sử dụng
chỉ báo này trong chuyên gia cố vấn, thì định dạng ở trên sẽ làm rõ chính xác chỉ mục
bộ đệm chỉ báo nào họ nên sử dụng cho mỗi dòng. Hàm start() của chỉ báo Trình
hướng dẫn chỉ chèn một biểu thức vào hàm start(): int count_bars =
IndicatorCounted(); IndicatorCounted() trả về số thanh trên biểu đồ mà chỉ báo đã tính
toán. Khi EA được khởi động lần đầu tiên, giá trị này sẽ là 0. Chỉ báo sẽ được tính cho
mọi thanh trên biểu đồ. Trên các thanh tiếp theo, chúng ta sẽ kiểm tra hàm
IndicatorCounted() để xem có bao nhiêu thanh đã được tính toán, từ đó chúng ta sẽ
biết chính xác có bao nhiêu thanh mới cần tính toán. Việc tính toán chỉ báo của chúng
tôi sẽ diễn ra bên trong vòng lặp for. Điểm bắt đầu sẽ là thanh chưa được tính toán
đầu tiên và điểm kết thúc sẽ là thanh hiện tại. Chúng ta sẽ so sánh giá trị của
IndicatorCounted() với biến Bars được xác định trước, biến này trả về số lượng thanh
trên biểu đồ hiện tại. Điều này sẽ xác định điểm khởi đầu của chúng tôi. Đây là mã cho
vòng lặp for: int count_bars = IndicatorCounted(); if(counted_bars > 0) count_bars--; int
CalculateBars = Thanh - count_bars; for(int Count = CalculateBars; Count >= 0; Count-
-) { // Tính toán chỉ báo } Câu lệnh if đầu tiên sẽ giảm giá trị của count_bars đi 1 khi
tính toán các thanh mới. Chúng tôi sẽ luôn tính toán ít nhất hai thanh trước đó. Điều
này là do điều kiện có thể không tính được dấu tích cuối cùng của thanh trong một số
trường hợp. Tiếp theo, chúng tôi xác định số lượng thanh cần tính toán bằng cách trừ
count_bars khỏi biến Bars được xác định trước. Điều này được lưu trữ trong biến
CalculateBars. 150 www.ZTCprep.com Chỉ báo và Tập lệnh Tùy chỉnh Trong vòng lặp
for của chúng tôi, biến tăng Count được đặt thành giá trị của CalculateBars, điều kiện
để kết thúc là khi Count nhỏ hơn 0 và biến Count được giảm dần sau mỗi lần lặp. Điều
này sẽ tính toán từng thanh trên biểu đồ từ trái sang phải. Đây là mã để tính Dải
Bollinger của chúng tôi. Chúng ta sẽ khai báo biến ngoài BandsPeriod ở đầu tệp. Vòng
lặp for là vòng lặp chúng ta đã tạo ở trên: // Các tham số bên ngoài extern int
BandsPeriod = 20; // hàm start() for(int Count = CalculateBars; Count >= 0; Count--) {
EMA[Count] = iMA(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); UpperBand[Count] = EMA[Count]
+ StdDev; LowerBand[Count] = EMA[Count] - StdDev; } Đầu tiên, chúng ta gọi chỉ báo
Trung bình trượt tích hợp bằng hàm iMA() và gán giá trị trả về cho EMA[Count]. Lưu ý
rằng chỉ mục mảng và tham số Shift cho chỉ báo trung bình động đều sử dụng giá trị
Đếm hiện tại. Tiếp theo, chúng tôi gọi chỉ báo Độ lệch chuẩn bằng iStdDev(). Để tính
dải trên, tất cả những gì chúng ta cần làm là cộng độ lệch chuẩn vào đường trung bình
động. Điều này được lưu trữ trong mảng đệm UpperBand[]. Để tính LowerBand[],
chúng tôi trừ đi độ lệch chuẩn khỏi mức trung bình động. Hãy mở rộng chỉ báo của
chúng tôi thêm một chút bằng cách cung cấp cho nó đầy đủ các cài đặt. Chúng tôi sẽ
thêm cài đặt để điều chỉnh dịch chuyển kỳ hạn, phương pháp trung bình động và các
tham số giá được áp dụng cũng như điều chỉnh độ lệch chuẩn: // Tham số bên ngoài
extern int BandsPeriod = 20; extern int BandsShift = 0; extern int BandsMethod = 1;
extern int BandsPrice = 0; extern int Độ lệch = 1; 151 www.ZTCprep.com LẬP TRÌNH
TƯ VẤN CHUYÊN NGHIỆP // start() function for(int Count = CalculateBars; Count >= 0;
Count--) { EMA[Count] =
iMA(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice ,Đếm); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice,Count);
UpperBand[Count] = EMA[Count] + (StdDev * Độ lệch); LowerBand[Count] =
EMA[Count] - (StdDev * Độ lệch); } Chúng tôi đã thêm các biến bên ngoài để điều chỉnh
các tham số còn lại cho hàm iMA() và iStdDev(). Chúng tôi cũng đã thêm một tham số
để điều chỉnh số lượng độ lệch chuẩn. Để tính toán điều này, chúng tôi chỉ cần nhân
StdDev với Độ lệch. Bây giờ chúng ta có chỉ báo Dải bollinger có thể điều chỉnh hoàn
toàn, linh hoạt hơn chỉ báo MetaTrader tiêu chuẩn. Mã đầy đủ được liệt kê trong Phụ
lục E. Bạn có thể làm được nhiều việc hơn với các chỉ báo tùy chỉnh thay vì chỉ tính
toán lại các chỉ báo tích hợp. Tùy thuộc vào trình độ kiến thức toán học của bạn, bạn
có thể mã hóa các chỉ báo không có trong MetaTrader hoặc thậm chí tạo chỉ báo của
riêng bạn. Bạn cũng có thể vẽ và thao tác các đối tượng. Nếu bạn muốn tìm hiểu thêm
về cách tạo chỉ báo tùy chỉnh, hãy xem chủ đề Tham khảo MQL Chỉ báo tùy chỉnh,
Hàm đối tượng và Toán & Lượng giác. Tập lệnh Tập lệnh là một chương trình MQL chỉ
chạy một lần khi nó được đính kèm lần đầu tiên vào biểu đồ. Tập lệnh có thể được sử
dụng để tự động hóa một loạt hành động giao dịch, chẳng hạn như đóng tất cả các
lệnh trên biểu đồ hoặc gửi lệnh chờ xử lý. Một số tập lệnh, chẳng hạn như tập lệnh
Period_converter đi kèm với MetaTrader, có thể vẽ lại biểu đồ dựa trên khoảng thời
gian tùy chỉnh. Tệp mã nguồn tập lệnh phải có chỉ thị thuộc tính show_confirm hoặc
show_inputs. Thuộc tính show_confirm nhắc người dùng xác nhận hoạt động của tập
lệnh, trong khi show_inputs hiển thị hộp thoại thuộc tính tập lệnh. #property
show_confirm // hiển thị hộp thoại xác nhận #property show_inputs // hiển thị hộp
thoại thuộc tính Nếu tập lệnh của bạn có các tham số cần điều chỉnh, hãy sử dụng
thuộc tính show_inputs. Nếu không, hãy sử dụng show_confirm. 152
www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Cũng giống như các chỉ báo và
cố vấn chuyên gia, các tập lệnh sử dụng các hàm init(), deinit() và start(). Hãy nhớ
rằng mỗi hàm sẽ chỉ được chạy một lần – init() và start() khi tập lệnh được khởi động
và deinit() khi tập lệnh bị xóa. Bạn có thể đính kèm một tập lệnh vào biểu đồ tại một
thời điểm. MetaTrader đi kèm với một số tập lệnh mẫu. Tất cả các tập lệnh được lưu
trong thư mục \experts\scripts. 153 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục A Cố vấn chuyên gia đơn giản Đây là chuyên gia cố vấn đơn
giản từ chương 2. #property bản quyền "Andrew Young" // Biến ngoài extern double
LotSize = 0.1; StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern
int Trượt = 5; extern int MagicNumber = 123; extern int FastMAPeriod = 10; extern int
SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int BánTicket; điểm sử dụng gấp
đôi; int UseTrượt trượt; // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol());
UseSlippage = GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường
trung bình động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi
SlowMA = iMA(NULL,0,SlowMAPeriod,0,0,0,0); 154 www.ZTCprep.com Phụ lục A //
Lệnh mua if(FastMA > SlowMA && BuyTicket == 0) {
OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0 &&
SellTicket > 0) { double CloseLots = OrderLots(); gấp đôi Giá đóng cửa = Hỏi; bool Đã
đóng = OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } double
OpenPrice = Hỏi; // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss
= OpenPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit =
OpenPrice + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Order",MagicNumber,0,Green); BánTicket = 0; } //
Lệnh bán if(FastMA < SlowMA && SellTicket == 0) {
OrderSelect(BuyTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0 && BuyTicket >
0) { CloseLots = OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } OpenPrice = Giá thầu;
if(StopLoss > 0) double SellStopLoss = OpenPrice + (StopLoss * UsePoint);
if(TakeProfit > 0) double SellTakeProfit = OpenPrice - (TakeProfit * UsePoint);
SellTicket = OrderSend(Symbol(),OP_SELL,LotSize,OpenPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Sell Order",MagicNumber,0,Red); MuaVé = 0; } 155
www.ZTCprep.com CHUYÊN GIA LẬP TRÌNH TƯ VẤN return(0); } // Hàm điểm Pip
nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits = MarketInfo(Tiền tệ,MODE_DIGITS);
if(CalcDigits == 2 || CalcDigits == 3) gấp đôi CalcPoint = 0,01; khác nếu(CalcDigits == 4
|| CalcDigits == 5) CalcPoint = 0,0001; return(CalcPoint); } // Lấy hàm trượt giá int
GetSlippage(string Money, int SlippagePips) { int CalcDigits =
MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 4) double
CalcSlippage = SlippagePips; else if(CalcDigits == 3 || CalcDigits == 5) CalcSlippage =
SlippagePips * 10; return(CalcSlippage); } Chuyên gia cố vấn đơn giản với các lệnh
đang chờ xử lý Đây là chuyên gia cố vấn đơn giản sử dụng các lệnh dừng đang chờ xử
lý: #property Copyright "Andrew Young" // Biến bên ngoài extern double LotSize = 0.1;
StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern int
PendingPips = 10; extern int Trượt = 5; extern int MagicNumber = 123; extern int
FastMAPeriod = 10; extern int SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int
BánTicket; điểm sử dụng gấp đôi; int UseTrượt trượt; 156 www.ZTCprep.com Phụ lục
A // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol()); UseSlippage =
GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường trung bình
động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,0); // Lệnh mua if(FastMA > SlowMA && BuyTicket ==
0) { OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0
&& SellTicket > 0 && OrderType() == OP_SELL) { double CloseLots = OrderLots(); gấp
đôi Giá đóng cửa = Hỏi; bool Đã đóng =
OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } // Xóa đơn hàng khác
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_SELLSTOP) { bool
Đã xóa = OrderDelete(SellTicket,Red); } double PendingPrice = Cao[0] + (PendingPips *
UsePoint); // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss =
PendingPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit = Đang
chờ xử lý + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Stop Order",MagicNumber,0,Green); BánTicket = 0; }
157 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA // Bán lệnh if(FastMA <
SlowMA && SellTicket == 0) { OrderSelect(BuyTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0 && BuyTicket > 0 && OrderType() == OP_BUY) { CloseLots =
OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } else
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_BUYSTOP) { Đã xóa
= OrderDelete(SellTicket,Red); } Giá đang chờ xử lý = Thấp[0] - (PendingPips *
UsePoint); if(StopLoss > 0) double SellStopLoss = Giá đang chờ xử lý + (StopLoss *
UsePoint); if(TakeProfit > 0) double SellTakeProfit = Đang chờ xử lý - (TakeProfit *
UsePoint); SellTicket =
OrderSend(Symbol(),OP_SELLSTOP,LotSize,PendingPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Lệnh dừng bán",MagicNumber,0,Red); MuaVé = 0; } trả
về(0); } // Hàm điểm Pip nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits =
MarketInfo(Tiền tệ,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 3) gấp đôi
CalcPoint = 0,01; khác nếu(CalcDigits == 4 || CalcDigits == 5) CalcPoint = 0,0001;
return(CalcPoint); } // Lấy hàm trượt giá int GetSlippage(string Money, int
SlippagePips) { int CalcDigits = MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2
|| CalcDigits == 4) double CalcSlippage = SlippagePips; else if(CalcDigits == 3 ||
CalcDigits == 5) CalcSlippage = SlippagePips * 10; return(CalcSlippage); } 158
www.ZTCprep.com Phụ lục A 159 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục B Cố vấn chuyên gia nâng cao Đây là chuyên gia cố vấn với
các tính năng nâng cao từ chương 3. #property Copyright "Andrew Young" #include
Hãy tạo một câu lệnh Print() và in nội dung của tất cả các biến có liên quan vào nhật
ký. Chúng tôi sẽ chạy EA trong Trình kiểm tra chiến lược, chỉ sử dụng Giá mở làm mô
hình thử nghiệm. Hãy đảm bảo rằng bạn đang thử nghiệm EA trong một khoảng thời
gian đủ dài để EA có thể đặt đủ giao dịch cho chúng tôi phân tích. Nếu bạn cần kiểm
tra giá trên biểu đồ, hãy nhấn nút Mở biểu đồ để mở biểu đồ hiển thị các giao dịch
được mô phỏng. Tiếp theo, chúng ta sẽ chuyển đến tab Nhật ký và kiểm tra thông tin
chúng ta cần. Nếu chúng ta cần xem toàn bộ nhật ký hoặc nếu có giao dịch không
hiển thị trong tab Nhật ký, chúng ta có thể nhấp chuột phải và chọn Mở từ menu bật
lên và mở tệp nhật ký trực tiếp. Mã này đang gây ra lỗi 130: "điểm dừng không hợp lệ"
mỗi khi chúng tôi đặt lệnh mua. Chúng tôi biết rằng lỗi 130 có nghĩa là lệnh dừng lỗ
hoặc chốt lãi không chính xác. Bạn có thể xác định được lỗi không? if(Close[0] > MA
&& BuyTicket == 0) { double Openprice = Hỏi; gấp đôi BuyStopLoss = OpenPrice +
(StopLoss * UsePoint); gấp đôi BuyTakeProfit = OpenPrice + (TakeProfit * UsePoint);
BuyTicket = OrderSend(Symbol(),OP_BUY,LotSize,Openprice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Mua đơn hàng",MagicNumber,0,Green); BánTicket = 0; }
Chúng ta sẽ sử dụng hàm Print() để xác minh các tham số đang được truyền cho hàm
OrderSend(). Chúng ta sẽ tập trung vào giá mở lệnh, mức dừng lỗ và chốt lời.
Print("Giá:"+OpenPrice+" Dừng:"+BuyStopLoss+" Lợi nhuận:"+BuyTakeProfit); Đây là
kết quả khi chúng tôi chạy EA trong trình thử nghiệm chiến lược. Giả sử mức dừng lỗ
và chốt lời là 50 pips: 11:52:12 2009.11.02 02:00 Ví dụ EURUSD,H1: Lỗi gửi lệnh 130
11:52:12 2009.11.02 02:00 Ví dụ EURUSD,H1: Giá:1.47340000 Dừng:1.47840000 Lợi
nhuận:1.47840000 Chúng tôi biết rằng mức dừng lỗ phải thấp hơn giá mở cửa cho
một lệnh mua. Ở đây, nó cao hơn giá. Trên thực tế, nó có cùng mức giá với mức chốt
lời. Xem nhanh mã của chúng tôi và chúng tôi nhận ra rằng chúng tôi đã vô tình chèn
dấu cộng vào phương trình dừng lỗ mua. Đây là mã chính xác: 142 www.ZTCprep.com
Mẹo và thủ thuật nhân đôi BuyStopLoss = OpenPrice - (StopLoss * UsePoint); Nếu bạn
nhận được thông báo lỗi khi cố gắng đặt, đóng hoặc sửa đổi đơn hàng, hãy tập trung
nỗ lực vào vấn đề được chỉ ra bởi thông báo lỗi. Dưới đây là một số thông báo lỗi phổ
biến nhất do lỗi lập trình gây ra: • Lỗi 129: Giá không hợp lệ – Giá mở cửa không hợp
lệ. Đối với các lệnh thị trường, hãy đảm bảo giá Mua hoặc Bán hiện tại đang được
thông qua, tùy theo loại lệnh. Đối với các lệnh đang chờ xử lý, hãy đảm bảo giá cao
hơn hoặc thấp hơn giá hiện tại, theo yêu cầu của loại lệnh. Đồng thời kiểm tra xem giá
lệnh chờ xử lý có quá gần với giá hiện tại hay không (tức là nằm trong mức dừng). • Lỗi
130: Điểm dừng không hợp lệ – Giá dừng lỗ hoặc chốt lãi không chính xác. Kiểm tra
xem giá dừng lỗ và chốt lời được đặt ở trên hay dưới giá hiện tại, tùy thuộc vào loại
lệnh là mua hay bán. Đồng thời kiểm tra xem giá dừng lỗ hoặc chốt lãi không quá gần
với giá hiện tại (tức là nằm trong mức dừng). • Lỗi 131: Khối lượng giao dịch không
hợp lệ – Kích thước lô không chính xác. Đảm bảo rằng kích thước lô không vượt quá
mức tối thiểu hoặc tối đa của nhà môi giới và kích thước lô được chuẩn hóa thành giá
trị bước chính xác (0,1 hoặc 0,01 trên hầu hết các nhà môi giới). Bạn có thể tìm thấy
mô tả của tất cả các thông báo lỗi trong Tham chiếu MQL trong Hằng số tiêu chuẩn –
Mã lỗi. Nếu bạn cần hỗ trợ thêm về lỗi mà bạn đang gặp phải, hãy kiểm tra các diễn
đàn tại MQL4.com. Khắc phục sự cố các lỗi giao dịch không liên tục Trong khi hầu hết
các lỗi nghiêm trọng có thể được tìm thấy đơn giản bằng cách kiểm tra lại, những lỗi
khác sẽ chỉ xảy ra trong quá trình giao dịch theo thời gian thực. Lỗi logic có thể dẫn
đến giao dịch không được đặt chính xác và bạn có thể phải mất một chút nỗ lực để
xác định những lỗi này. Nếu có giao dịch được đặt không chính xác trong quá trình
giao dịch demo hoặc giao dịch trực tiếp, chúng tôi cần nhiều thông tin cần thiết để
khắc phục sự cố. Chúng tôi sẽ thêm một tính năng tùy chọn để ghi nhật ký thông tin
trạng thái và giao dịch theo thời gian thực để chúng tôi có bản ghi về nó khi khắc phục
sự cố giao dịch. Chúng tôi sẽ sử dụng câu lệnh Print() như trước đây nhưng chúng tôi
sẽ ghi lại các giá trị chỉ báo, giá cả – bất kỳ thông tin nào hữu ích trong việc gỡ lỗi.
Chúng tôi cũng sẽ thêm một biến bên ngoài để bật và tắt đăng nhập. // Biến ngoài
extern bool Debug = true; 143 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN
NGHIỆP // Đặt ở gần cuối hàm start() if(Debug == true)
Print(StringConcatenate("Bid:",Bid," Ask:",Ask," MA:" ,MA, " MuaTicket:",BuyTicket,"
SellTicket:",SellTicket)); Đoạn mã trên sẽ ghi lại thông tin về giá và chỉ báo cũng như
nội dung của các biến BuyTicket và SellTicket. Nếu có bất kỳ câu hỏi nào về cách mở
giao dịch hoặc tại sao giao dịch không được mở, nhật ký tại thời điểm cụ thể đó sẽ
hiển thị trạng thái của tất cả các điều kiện giao dịch có liên quan. Bạn có thể bật và tắt
đăng nhập bằng biến ngoài Debug. Câu lệnh debug Print() phải được đặt ở gần cuối
hàm start(), sau tất cả các hàm giao dịch. Nếu bạn đang sử dụng bộ hẹn giờ và/hoặc
tính năng thực thi khi mở thanh, hãy đặt câu lệnh debug Print() bên trong khối bộ hẹn
giờ để nó chỉ chạy khi cần thiết. Nếu không, dòng gỡ lỗi sẽ in ra nhật ký sau mỗi tích
tắc, điều này có thể tạo ra một tệp nhật ký lớn. Sửa lỗi biên dịch Khi bạn biên dịch
Expert Advisor, trình biên dịch sẽ kiểm tra cú pháp đúng và đảm bảo rằng tất cả các
hàm và biến tùy chỉnh đã được khai báo đúng. Nếu bạn bỏ sót nội dung nào đó, trình
biên dịch sẽ dừng và mọi lỗi biên dịch sẽ xuất hiện trong tab Lỗi trong cửa sổ Hộp
công cụ. Khi gặp phải một danh sách dài các lỗi biên dịch, hãy luôn bắt đầu từ lỗi đầu
tiên. Bấm đúp vào lỗi trong danh sách và trình soạn thảo sẽ chuyển trực tiếp đến dòng
có lỗi. Sửa lỗi và biên dịch lại. Đôi khi một lỗi cú pháp đơn giản sẽ dẫn đến một số lỗi
không liên quan, mặc dù chỉ có lỗi đầu tiên là hợp lệ. Đây là danh sách các lỗi biên
dịch thường gặp và cách khắc phục: • Biến không được xác định – Bạn quên khai báo
một biến có kiểu dữ liệu. Nếu đó là biến toàn cục hoặc biến ngoài, hãy khai báo nó ở
đầu tệp. Nếu đó là biến cục bộ, hãy tìm lần xuất hiện đầu tiên và đặt phần khai báo
kiểu dữ liệu trước biến đó. Nếu không, hãy kiểm tra chính tả hoặc kiểu chữ
(hoa/thường) của tên biến. • Biến đã được xác định – Bạn đã khai báo cùng một biến
hai lần. Loại bỏ phần khai báo kiểu dữ liệu khỏi tất cả các khai báo biến trùng lặp. •
Hàm không được xác định – Nếu hàm được đề cập nằm trong tệp bao gồm hoặc thư
viện, hãy đảm bảo rằng lệnh #include hoặc #import được đặt ở đầu tệp và đúng. Nếu
không, hãy kiểm tra chính tả hoặc kiểu chữ của tên hàm và đảm bảo rằng nó tồn tại
trong tệp hiện tại hoặc trong tệp bao gồm hoặc thư viện có liên quan. 144
www.ZTCprep.com Mẹo và thủ thuật • Phép gán bất hợp pháp được sử dụng – Điều
này thường liên quan đến dấu bằng (=). Hãy nhớ rằng một dấu bằng duy nhất dành
cho phép gán biến và hai dấu bằng (==) là toán tử so sánh. Sửa toán tử gán thành toán
tử so sánh thích hợp. • Phép gán dự kiến – Điều này thường liên quan đến toán tử so
sánh "bằng" (==). Bạn đã sử dụng hai dấu bằng thay vì một trong phép gán biến. Sửa
toán tử thành một dấu bằng duy nhất. • Dấu ngoặc đơn bên phải không cân bằng –
Điều này thường xảy ra trong câu lệnh if khi sử dụng dấu ngoặc đơn lồng nhau. Đi đến
dòng được chỉ ra bởi lỗi đầu tiên và chèn dấu ngoặc đơn bên trái vào vị trí thích hợp. •
Dấu ngoặc trái không cân bằng – Đây là một dấu ngoặc đơn khó. Lỗi thường xảy ra ở
cuối dòng chương trình. Về cơ bản bạn đã quên dấu ngoặc đơn ở đâu đó. Kiểm tra kỹ
mã bạn đã chỉnh sửa gần đây và tìm dấu ngoặc đơn bên phải bị thiếu. Bạn có thể phải
nhận xét các dòng mã để xác định vấn đề. • Đếm tham số sai – Bạn có quá ít hoặc
quá nhiều đối số trong một hàm. Kiểm tra kỹ cú pháp hàm trong Tham chiếu MQL và
sửa các đối số. • Cần có dấu chấm phẩy – Có thể bạn đã quên đặt dấu chấm phẩy ở
cuối dòng. Đặt dấu chấm phẩy ở cuối dòng trước. Lưu ý rằng thiếu dấu chấm phẩy
cũng có thể gây ra bất kỳ lỗi nào ở trên, vì vậy hãy nhớ đặt các dấu chấm phẩy đó! 145
www.ZTCprep.com CHƯƠNG TRÌNH CỐ VẤN CHUYÊN GIA Chương 9 Các chỉ báo và
tập lệnh tùy chỉnh Không có cuốn sách nào về MQL sẽ hoàn chỉnh nếu không đề cập
đến các chỉ báo và tập lệnh tùy chỉnh. Các chỉ báo tích hợp trong MetaTrader khá hạn
chế, nhưng may mắn thay MQL cho phép các lập trình viên tạo các chỉ báo của riêng
họ. Nếu bạn đang tìm kiếm một chỉ báo phổ biến không có trong MT4 thì rất có thể ai
đó đã tạo một chỉ báo đó. Chương này sẽ là tổng quan cơ bản về việc tạo chỉ báo tùy
chỉnh. Hầu hết các chỉ báo đều sử dụng các công thức toán học phức tạp và đó là lĩnh
vực của các lập trình viên giàu kinh nghiệm hơn. Tuy nhiên, một chỉ báo không cần
phải phức tạp. Chúng ta sẽ tạo một chỉ báo tùy chỉnh trong chương này chỉ sử dụng
một vài dòng mã. Bộ đệm Bộ đệm là mảng lưu trữ các giá trị chỉ báo và phép tính. Một
chỉ báo tùy chỉnh có thể có tối đa 8 bộ đệm. Bộ đệm sử dụng các chỉ mục, giống như
mảng và có phạm vi từ 0 đến 7. Khi bạn gọi một chỉ báo tùy chỉnh trong chuyên gia tư
vấn bằng cách sử dụng hàm iCustom(), tham số tiếp theo trong hàm là bộ đệm chỉ
báo. Để tìm vùng đệm thích hợp cho dòng chỉ báo, bạn thường kiểm tra mã nguồn, nếu
có. Nếu mã nguồn được định dạng rõ ràng với các tên biến mô tả, bạn sẽ có thể xác
định bộ đệm thích hợp khá dễ dàng. Chúng ta sẽ đề cập đến cách đặt tên thích hợp
cho bộ đệm chỉ báo trong chương này. Tạo chỉ báo tùy chỉnh Hãy xây dựng một chỉ
báo tùy chỉnh bằng cách sử dụng hai chỉ báo MetaTrader tích hợp để tính toán các
đường của chúng tôi. Chúng ta sẽ xây dựng một chỉ báo Dải Bollinger đã được sửa
đổi. Dải Bollinger bao gồm 3 đường – đường trung tâm là đường trung bình động đơn
giản, cùng với đường trên và đường dưới có giá trị được xác định bằng độ lệch chuẩn.
Chúng ta có thể tạo chỉ báo Dải bollinger của riêng mình bằng cách sử dụng các chỉ
báo Trung bình trượt và Độ lệch chuẩn. Chúng tôi muốn tạo một chỉ báo sử dụng
đường trung bình động hàm mũ để tính toán các đường, trái ngược với đường trung
bình động đơn giản. 146 www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Chúng
tôi bắt đầu bằng cách sử dụng trình hướng dẫn để tạo tệp chỉ báo của mình. Chọn Mới
từ menu Tệp hoặc thanh công cụ để mở trình hướng dẫn và tạo chỉ báo tùy chỉnh. Điền
tên chỉ báo và thêm thông số nếu muốn. Trên trang cuối cùng, chúng tôi đã thêm ba
dòng chỉ báo cùng màu. Đây là kết quả của thuật sĩ. Hiện tại chúng ta đã bỏ qua hàm
start(): //+--------------------------------- --------------------------------+ //| EMA Bollinger.mq4 | //|
Andrew trẻ | //| http://www.easyexpertforex.com | //+-----------------------------------------------------
-- -------------------+ #bản quyền tài sản "Andrew Young" #link tài sản
"http://www.easyexpertforex.com" #property Indicator_chart_window #property
Indicator_buffers 3 #property Indicator_color1 DeepSkyBlue #property
Indicator_color2 DeepSkyBlue #property Indicator_color3 DeepSkyBlue //---- đệm gấp
đôi ExtMapBuffer1[]; nhân đôi ExtMapBuffer2[]; nhân đôi ExtMapBuffer3[]; //+--------------
----------------------------------------- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+---
---------------------------------------------------- -------------------+ int init() { //---- chỉ báo
SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,ExtMapBuffer1);
SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,ExtMapBuffer2);
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,ExtMapBuffer3); //---- return(0); } Hãy
chuyển sự chú ý của chúng ta tới các phần tử được in đậm. Khai báo #property đặt
tham số cho bộ đệm chỉ báo của chúng tôi. Thuộc tính Indicator_chart_window vẽ chỉ
báo của chúng tôi trong cửa sổ biểu đồ chính. Nếu chúng ta đang tạo một bộ dao
động và muốn vẽ chỉ báo trong một cửa sổ riêng biệt, thì thay vào đó, chúng ta sẽ sử
dụng thuộc tính Indicator_separate_window. Thuộc tính Indicator_buffers đặt số lượng
bộ đệm cho chỉ báo của chúng tôi. Trong trường hợp này chúng tôi đang sử dụng ba
bộ đệm. Thuộc tính Indicator_color đặt màu của cả ba dòng thành DeepSkyBlue. 147
www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN NGHIỆP Tiếp theo là các khai báo
cho mảng bộ đệm của chúng ta. Chúng tôi có ba bộ đệm có tên ExtMapBuffer(1-3).
Chúng tôi sẽ sớm thay đổi các mã định danh mảng này thành một cái gì đó mang tính
mô tả hơn. Hàm init() là nơi chúng ta đặt các thuộc tính cho bộ đệm chỉ báo của mình.
SetIndexBuffer() liên kết một mảng bộ đệm với một chỉ mục bộ đệm. Chỉ mục bộ đệm
là những gì chúng tôi đề cập đến khi đặt thuộc tính cho một dòng chỉ báo cũng như
khi chúng tôi gọi một dòng chỉ báo từ EA bằng cách sử dụng hàm iCustom(). Tham số
đầu tiên là một số nguyên từ 0 đến 7 và tham số thứ hai là tên của mảng đệm. Các
thuộc tính của bản vẽ Hàm SetIndexStyle() thiết lập loại đường để vẽ, cùng với các
thuộc tính của đường đó. Mỗi dòng chỉ báo sẽ có hàm SetIndexStyle() tương ứng. Đây
là cú pháp: void SetIndexStyle(int BufferIndex, int LineType, int LineStyle = EMPTY, int
LineWidth = EMPTY, color LineColor = CLR_NONE) • BufferIndex – Chỉ mục của bộ
đệm, từ 0 đến 7. • LineType – Đặt loại bộ đệm đường để vẽ. DRAW_LINE vẽ một
đường duy nhất, DRAW_HISTOGRAM vẽ biểu đồ dọc (xem ví dụ về OsMA hoặc Bộ tạo
dao động tuyệt vời), DRAW_ARROW vẽ một biểu tượng và DRAW_NONE không vẽ
đường nào. • LineStyle – Một tham số tùy chọn cho biết kiểu vẽ. Được sử dụng chủ
yếu cho các dòng loại DRAW_LINE. Theo mặc định, một đường liền nét được vẽ
(STYLE_SOLID). Bạn cũng có thể vẽ các đường đứt nét (STYLE_DASH) và chấm
(STYLE_DOT). • LineWidth – Một tham số tùy chọn cho biết chiều rộng của đường tính
bằng pixel. Giá trị mặc định là 1. • LineColor – Một tham số tùy chọn cho biết màu của
đường. Nếu bạn sử dụng trình hướng dẫn, màu sẽ được đặt bằng cách sử dụng khai
báo #property, nhưng bạn cũng có thể đặt màu ở đây. Nếu bạn đang sử dụng
DRAW_ARROW làm LineType, hàm SetArrow() cho phép bạn đặt ký hiệu phông chữ
Wingdings để vẽ trên biểu đồ. Tham số đầu tiên là chỉ mục bộ đệm và tham số thứ hai
là hằng số nguyên biểu thị ký hiệu cần vẽ. Các ký hiệu có thể được tìm thấy trong
Tham chiếu MQL trong Hằng số tiêu chuẩn – Mã mũi tên. Bạn có thể muốn thêm mô
tả cho các dòng chỉ báo sẽ được hiển thị trong chú giải công cụ hoặc trong cửa sổ dữ
liệu. Để thực hiện việc này, hãy sử dụng hàm SetIndexLabel(). Tham số đầu tiên là chỉ
mục bộ đệm và tham số thứ hai là mô tả văn bản. Chúng tôi sẽ sớm thêm những thứ
này vào chỉ báo của chúng tôi. 148 www.ZTCprep.com Các chỉ báo và tập lệnh tùy
chỉnh Nếu chỉ báo của bạn được vẽ trong một cửa sổ riêng biệt (chẳng hạn như bộ
dao động) và bạn muốn thêm các mức để biểu thị mức quá mua hoặc quá bán (chẳng
hạn như trong các chỉ báo Stochastic hoặc RSI) hoặc mức 0 (chẳng hạn như trong chỉ
báo CCI), bạn có thể sử dụng các hàm SetLevelStyle() và SetLevelValue(). Xem Tham
chiếu MQL trong Chỉ báo tùy chỉnh để biết thêm thông tin. Bạn cũng có thể muốn chỉ
định một tên chỉ báo ngắn gọn sẽ được hiển thị ở góc trên cùng bên trái của cửa sổ
chỉ báo. Sử dụng hàm IndicatorShortName() để đặt giá trị này. Tham số duy nhất là
một chuỗi văn bản sẽ xuất hiện ở góc trên bên trái của cửa sổ chỉ báo, cũng như trong
cửa sổ dữ liệu. Sử dụng tên bộ đệm mô tả Đây là mã chỉ báo được cập nhật của chúng
tôi. Lưu ý rằng chúng tôi đã đổi tên các mảng đệm để mang tính mô tả rõ hơn về chức
năng thực tế của chúng. Chúng tôi đã thay đổi tham số thứ hai của hàm
SetIndexBuffer() để phản ánh tên bộ đệm mới. Chúng tôi cũng đã thêm
SetIndexLabel() cho mỗi dòng để hiển thị tên mô tả trong Cửa sổ dữ liệu. //---- đệm đôi
EMA[]; gấp đôi UpperBand[]; gấp đôi LowerBand[]; //+-----------------------------------------------------
-- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+------------------------------------------
------------- -------------------+ int init() { //---- chỉ báo SetIndexStyle(0,DRAW_LINE);
SetIndexBuffer(0,EMA); SetIndexLabel(0,"EMA"); SetIndexStyle(1,DRAW_LINE);
SetIndexBuffer(1,UpperBand); SetIndexLabel(1,"UpperBand");
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,LowerBand);
SetIndexLabel(2,"LowerBand"); //---- return(0); } Chúng tôi đã đổi tên các mảng vùng
đệm từ tên mặc định (ExtMapBuffer) thành tên mang tính mô tả hơn. EMA[] sẽ là vùng
đệm của chúng tôi cho đường trung tâm và UpperBand[] và LowerBand[] sẽ lần lượt là
dải trên và dải dưới. 149 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Các
hàm SetIndexBuffer() liên kết các chỉ mục bộ đệm với mảng bộ đệm của chúng ta.
EMA là 0, UpperBand là 1 và LowerBand là 2. Lưu ý rằng các dấu ngoặc nhọn bị loại
khỏi tên định danh mảng cho tham số SetIndexBuffer() thứ hai. Các hàm
SetIndexLabel() đặt tên mô tả cho từng vùng đệm chỉ báo. Trong trường hợp này, tên
dòng giống với tên định danh của chúng tôi. Chúng sẽ xuất hiện trên chú giải công cụ
chuột cũng như trong Cửa sổ dữ liệu. Nếu một lập trình viên khác quyết định sử dụng
chỉ báo này trong chuyên gia cố vấn, thì định dạng ở trên sẽ làm rõ chính xác chỉ mục
bộ đệm chỉ báo nào họ nên sử dụng cho mỗi dòng. Hàm start() của chỉ báo Trình
hướng dẫn chỉ chèn một biểu thức vào hàm start(): int count_bars =
IndicatorCounted(); IndicatorCounted() trả về số thanh trên biểu đồ mà chỉ báo đã tính
toán. Khi EA được khởi động lần đầu tiên, giá trị này sẽ là 0. Chỉ báo sẽ được tính cho
mọi thanh trên biểu đồ. Trên các thanh tiếp theo, chúng ta sẽ kiểm tra hàm
IndicatorCounted() để xem có bao nhiêu thanh đã được tính toán, từ đó chúng ta sẽ
biết chính xác có bao nhiêu thanh mới cần tính toán. Việc tính toán chỉ báo của chúng
tôi sẽ diễn ra bên trong vòng lặp for. Điểm bắt đầu sẽ là thanh chưa được tính toán
đầu tiên và điểm kết thúc sẽ là thanh hiện tại. Chúng ta sẽ so sánh giá trị của
IndicatorCounted() với biến Bars được xác định trước, biến này trả về số lượng thanh
trên biểu đồ hiện tại. Điều này sẽ xác định điểm khởi đầu của chúng tôi. Đây là mã cho
vòng lặp for: int count_bars = IndicatorCounted(); if(counted_bars > 0) count_bars--; int
CalculateBars = Thanh - count_bars; for(int Count = CalculateBars; Count >= 0; Count-
-) { // Tính toán chỉ báo } Câu lệnh if đầu tiên sẽ giảm giá trị của count_bars đi 1 khi
tính toán các thanh mới. Chúng tôi sẽ luôn tính toán ít nhất hai thanh trước đó. Điều
này là do điều kiện có thể không tính được dấu tích cuối cùng của thanh trong một số
trường hợp. Tiếp theo, chúng tôi xác định số lượng thanh cần tính toán bằng cách trừ
count_bars khỏi biến Bars được xác định trước. Điều này được lưu trữ trong biến
CalculateBars. 150 www.ZTCprep.com Chỉ báo và Tập lệnh Tùy chỉnh Trong vòng lặp
for của chúng tôi, biến tăng Count được đặt thành giá trị của CalculateBars, điều kiện
để kết thúc là khi Count nhỏ hơn 0 và biến Count được giảm dần sau mỗi lần lặp. Điều
này sẽ tính toán từng thanh trên biểu đồ từ trái sang phải. Đây là mã để tính Dải
Bollinger của chúng tôi. Chúng ta sẽ khai báo biến ngoài BandsPeriod ở đầu tệp. Vòng
lặp for là vòng lặp chúng ta đã tạo ở trên: // Các tham số bên ngoài extern int
BandsPeriod = 20; // hàm start() for(int Count = CalculateBars; Count >= 0; Count--) {
EMA[Count] = iMA(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); UpperBand[Count] = EMA[Count]
+ StdDev; LowerBand[Count] = EMA[Count] - StdDev; } Đầu tiên, chúng ta gọi chỉ báo
Trung bình trượt tích hợp bằng hàm iMA() và gán giá trị trả về cho EMA[Count]. Lưu ý
rằng chỉ mục mảng và tham số Shift cho chỉ báo trung bình động đều sử dụng giá trị
Đếm hiện tại. Tiếp theo, chúng tôi gọi chỉ báo Độ lệch chuẩn bằng iStdDev(). Để tính
dải trên, tất cả những gì chúng ta cần làm là cộng độ lệch chuẩn vào đường trung bình
động. Điều này được lưu trữ trong mảng đệm UpperBand[]. Để tính LowerBand[],
chúng tôi trừ đi độ lệch chuẩn khỏi mức trung bình động. Hãy mở rộng chỉ báo của
chúng tôi thêm một chút bằng cách cung cấp cho nó đầy đủ các cài đặt. Chúng tôi sẽ
thêm cài đặt để điều chỉnh dịch chuyển kỳ hạn, phương pháp trung bình động và các
tham số giá được áp dụng cũng như điều chỉnh độ lệch chuẩn: // Tham số bên ngoài
extern int BandsPeriod = 20; extern int BandsShift = 0; extern int BandsMethod = 1;
extern int BandsPrice = 0; extern int Độ lệch = 1; 151 www.ZTCprep.com LẬP TRÌNH
TƯ VẤN CHUYÊN NGHIỆP // start() function for(int Count = CalculateBars; Count >= 0;
Count--) { EMA[Count] =
iMA(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice ,Đếm); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice,Count);
UpperBand[Count] = EMA[Count] + (StdDev * Độ lệch); LowerBand[Count] =
EMA[Count] - (StdDev * Độ lệch); } Chúng tôi đã thêm các biến bên ngoài để điều chỉnh
các tham số còn lại cho hàm iMA() và iStdDev(). Chúng tôi cũng đã thêm một tham số
để điều chỉnh số lượng độ lệch chuẩn. Để tính toán điều này, chúng tôi chỉ cần nhân
StdDev với Độ lệch. Bây giờ chúng ta có chỉ báo Dải bollinger có thể điều chỉnh hoàn
toàn, linh hoạt hơn chỉ báo MetaTrader tiêu chuẩn. Mã đầy đủ được liệt kê trong Phụ
lục E. Bạn có thể làm được nhiều việc hơn với các chỉ báo tùy chỉnh thay vì chỉ tính
toán lại các chỉ báo tích hợp. Tùy thuộc vào trình độ kiến thức toán học của bạn, bạn
có thể mã hóa các chỉ báo không có trong MetaTrader hoặc thậm chí tạo chỉ báo của
riêng bạn. Bạn cũng có thể vẽ và thao tác các đối tượng. Nếu bạn muốn tìm hiểu thêm
về cách tạo chỉ báo tùy chỉnh, hãy xem chủ đề Tham khảo MQL Chỉ báo tùy chỉnh,
Hàm đối tượng và Toán & Lượng giác. Tập lệnh Tập lệnh là một chương trình MQL chỉ
chạy một lần khi nó được đính kèm lần đầu tiên vào biểu đồ. Tập lệnh có thể được sử
dụng để tự động hóa một loạt hành động giao dịch, chẳng hạn như đóng tất cả các
lệnh trên biểu đồ hoặc gửi lệnh chờ xử lý. Một số tập lệnh, chẳng hạn như tập lệnh
Period_converter đi kèm với MetaTrader, có thể vẽ lại biểu đồ dựa trên khoảng thời
gian tùy chỉnh. Tệp mã nguồn tập lệnh phải có chỉ thị thuộc tính show_confirm hoặc
show_inputs. Thuộc tính show_confirm nhắc người dùng xác nhận hoạt động của tập
lệnh, trong khi show_inputs hiển thị hộp thoại thuộc tính tập lệnh. #property
show_confirm // hiển thị hộp thoại xác nhận #property show_inputs // hiển thị hộp
thoại thuộc tính Nếu tập lệnh của bạn có các tham số cần được điều chỉnh, hãy sử
dụng thuộc tính show_inputs. Nếu không, hãy sử dụng show_confirm. 152
www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Cũng giống như các chỉ báo và
cố vấn chuyên gia, các tập lệnh sử dụng các hàm init(), deinit() và start(). Hãy nhớ
rằng mỗi hàm sẽ chỉ được chạy một lần – init() và start() khi tập lệnh được khởi động
và deinit() khi tập lệnh bị xóa. Bạn có thể đính kèm một tập lệnh vào biểu đồ tại một
thời điểm. MetaTrader đi kèm với một số tập lệnh mẫu. Tất cả các tập lệnh được lưu
trong thư mục \experts\scripts. 153 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục A Cố vấn chuyên gia đơn giản Đây là chuyên gia cố vấn đơn
giản từ chương 2. #property bản quyền "Andrew Young" // Biến ngoài extern double
LotSize = 0.1; StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern
int Trượt = 5; extern int MagicNumber = 123; extern int FastMAPeriod = 10; extern int
SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int BánTicket; điểm sử dụng gấp
đôi; int UseTrượt trượt; // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol());
UseSlippage = GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường
trung bình động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi
SlowMA = iMA(NULL,0,SlowMAPeriod,0,0,0,0); 154 www.ZTCprep.com Phụ lục A //
Lệnh mua if(FastMA > SlowMA && BuyTicket == 0) {
OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0 &&
SellTicket > 0) { double CloseLots = OrderLots(); gấp đôi Giá đóng cửa = Hỏi; bool Đã
đóng = OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } double
OpenPrice = Hỏi; // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss
= OpenPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit =
OpenPrice + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Order",MagicNumber,0,Green); BánTicket = 0; } //
Lệnh bán if(FastMA < SlowMA && SellTicket == 0) {
OrderSelect(BuyTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0 && BuyTicket >
0) { CloseLots = OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } OpenPrice = Giá thầu;
if(StopLoss > 0) double SellStopLoss = OpenPrice + (StopLoss * UsePoint);
if(TakeProfit > 0) double SellTakeProfit = OpenPrice - (TakeProfit * UsePoint);
SellTicket = OrderSend(Symbol(),OP_SELL,LotSize,OpenPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Sell Order",MagicNumber,0,Red); MuaVé = 0; } 155
www.ZTCprep.com CHUYÊN GIA LẬP TRÌNH TƯ VẤN return(0); } // Hàm điểm Pip
nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits = MarketInfo(Tiền tệ,MODE_DIGITS);
if(CalcDigits == 2 || CalcDigits == 3) gấp đôi CalcPoint = 0,01; khác nếu(CalcDigits == 4
|| CalcDigits == 5) CalcPoint = 0,0001; return(CalcPoint); } // Lấy hàm trượt giá int
GetSlippage(string Money, int SlippagePips) { int CalcDigits =
MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 4) double
CalcSlippage = SlippagePips; else if(CalcDigits == 3 || CalcDigits == 5) CalcSlippage =
SlippagePips * 10; return(CalcSlippage); } Chuyên gia cố vấn đơn giản với các lệnh
đang chờ xử lý Đây là chuyên gia cố vấn đơn giản sử dụng các lệnh dừng đang chờ xử
lý: #property Copyright "Andrew Young" // Biến bên ngoài extern double LotSize = 0.1;
StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern int
PendingPips = 10; extern int Trượt = 5; extern int MagicNumber = 123; extern int
FastMAPeriod = 10; extern int SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int
BánTicket; điểm sử dụng gấp đôi; int UseTrượt trượt; 156 www.ZTCprep.com Phụ lục
A // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol()); UseSlippage =
GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường trung bình
động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,0); // Lệnh mua if(FastMA > SlowMA && BuyTicket ==
0) { OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0
&& SellTicket > 0 && OrderType() == OP_SELL) { double CloseLots = OrderLots(); gấp
đôi Giá đóng cửa = Hỏi; bool Đã đóng =
OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } // Xóa đơn hàng khác
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_SELLSTOP) { bool
Đã xóa = OrderDelete(SellTicket,Red); } double PendingPrice = Cao[0] + (PendingPips *
UsePoint); // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss =
PendingPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit = Đang
chờ xử lý + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Stop Order",MagicNumber,0,Green); BánTicket = 0; }
157 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA // Bán lệnh if(FastMA <
SlowMA && SellTicket == 0) { OrderSelect(BuyTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0 && BuyTicket > 0 && OrderType() == OP_BUY) { CloseLots =
OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } else
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_BUYSTOP) { Đã xóa
= OrderDelete(SellTicket,Red); } Giá đang chờ xử lý = Thấp[0] - (PendingPips *
UsePoint); if(StopLoss > 0) double SellStopLoss = Giá đang chờ xử lý + (StopLoss *
UsePoint); if(TakeProfit > 0) double SellTakeProfit = Đang chờ xử lý - (TakeProfit *
UsePoint); SellTicket =
OrderSend(Symbol(),OP_SELLSTOP,LotSize,PendingPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Lệnh dừng bán",MagicNumber,0,Red); MuaVé = 0; } trả
về(0); } // Hàm điểm Pip nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits =
MarketInfo(Tiền tệ,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 3) gấp đôi
CalcPoint = 0,01; khác nếu(CalcDigits == 4 || CalcDigits == 5) CalcPoint = 0,0001;
return(CalcPoint); } // Lấy hàm trượt giá int GetSlippage(string Money, int
SlippagePips) { int CalcDigits = MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2
|| CalcDigits == 4) double CalcSlippage = SlippagePips; else if(CalcDigits == 3 ||
CalcDigits == 5) CalcSlippage = SlippagePips * 10; return(CalcSlippage); } 158
www.ZTCprep.com Phụ lục A 159 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục B Cố vấn chuyên gia nâng cao Đây là chuyên gia cố vấn với
các tính năng nâng cao từ chương 3. #property Copyright "Andrew Young" #include
"điểm dừng không hợp lệ" mỗi khi chúng tôi đặt lệnh mua. Chúng tôi biết rằng lỗi 130
có nghĩa là lệnh dừng lỗ hoặc chốt lãi không chính xác. Bạn có thể xác định được lỗi
không? if(Close[0] > MA && BuyTicket == 0) { double Openprice = Hỏi; gấp đôi
BuyStopLoss = OpenPrice + (StopLoss * UsePoint); gấp đôi BuyTakeProfit = OpenPrice
+ (TakeProfit * UsePoint); BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,Openprice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Mua đơn hàng",MagicNumber,0,Green); BánTicket = 0; }
Chúng ta sẽ sử dụng hàm Print() để xác minh các tham số đang được truyền cho hàm
OrderSend(). Chúng ta sẽ tập trung vào giá mở lệnh, mức dừng lỗ và chốt lời.
Print("Giá:"+OpenPrice+" Dừng:"+BuyStopLoss+" Lợi nhuận:"+BuyTakeProfit); Đây là
kết quả khi chúng tôi chạy EA trong trình thử nghiệm chiến lược. Giả sử mức dừng lỗ
và chốt lời là 50 pips: 11:52:12 2009.11.02 02:00 Ví dụ EURUSD,H1: Lỗi gửi lệnh 130
11:52:12 2009.11.02 02:00 Ví dụ EURUSD,H1: Giá:1.47340000 Dừng:1.47840000 Lợi
nhuận:1.47840000 Chúng tôi biết rằng mức dừng lỗ phải thấp hơn giá mở cửa cho
một lệnh mua. Ở đây, nó cao hơn giá. Trên thực tế, nó có cùng mức giá với mức chốt
lời. Xem nhanh mã của chúng tôi và chúng tôi nhận ra rằng chúng tôi đã vô tình chèn
dấu cộng vào phương trình dừng lỗ mua. Đây là mã chính xác: 142 www.ZTCprep.com
Mẹo và thủ thuật nhân đôi BuyStopLoss = OpenPrice - (StopLoss * UsePoint); Nếu bạn
nhận được thông báo lỗi khi cố gắng đặt, đóng hoặc sửa đổi đơn hàng, hãy tập trung
nỗ lực vào vấn đề được chỉ ra bởi thông báo lỗi. Dưới đây là một số thông báo lỗi phổ
biến nhất do lỗi lập trình gây ra: • Lỗi 129: Giá không hợp lệ – Giá mở cửa không hợp
lệ. Đối với các lệnh thị trường, hãy đảm bảo giá Mua hoặc Bán hiện tại đang được
thông qua, tùy theo loại lệnh. Đối với các lệnh đang chờ xử lý, hãy đảm bảo giá cao
hơn hoặc thấp hơn giá hiện tại, theo yêu cầu của loại lệnh. Đồng thời kiểm tra xem giá
lệnh chờ xử lý có quá gần với giá hiện tại hay không (tức là nằm trong mức dừng). • Lỗi
130: Điểm dừng không hợp lệ – Giá dừng lỗ hoặc chốt lãi không chính xác. Kiểm tra
xem giá dừng lỗ và chốt lời được đặt ở trên hay dưới giá hiện tại, tùy thuộc vào loại
lệnh là mua hay bán. Đồng thời kiểm tra xem giá dừng lỗ hoặc chốt lãi không quá gần
với giá hiện tại (tức là nằm trong mức dừng). • Lỗi 131: Khối lượng giao dịch không
hợp lệ – Kích thước lô không chính xác. Đảm bảo rằng kích thước lô không vượt quá
mức tối thiểu hoặc tối đa của nhà môi giới và kích thước lô được chuẩn hóa thành giá
trị bước chính xác (0,1 hoặc 0,01 trên hầu hết các nhà môi giới). Bạn có thể tìm thấy
mô tả của tất cả các thông báo lỗi trong Tham chiếu MQL trong Hằng số tiêu chuẩn –
Mã lỗi. Nếu bạn cần hỗ trợ thêm về lỗi mà bạn đang gặp phải, hãy kiểm tra các diễn
đàn tại MQL4.com. Khắc phục sự cố các lỗi giao dịch không liên tục Trong khi hầu hết
các lỗi nghiêm trọng có thể được tìm thấy đơn giản bằng cách kiểm tra lại, những lỗi
khác sẽ chỉ xảy ra trong quá trình giao dịch theo thời gian thực. Lỗi logic có thể dẫn
đến giao dịch không được đặt chính xác và bạn có thể phải mất một chút nỗ lực để
xác định những lỗi này. Nếu có giao dịch được đặt không chính xác trong quá trình
giao dịch demo hoặc giao dịch trực tiếp, chúng tôi cần nhiều thông tin cần thiết để
khắc phục sự cố. Chúng tôi sẽ thêm một tính năng tùy chọn để ghi nhật ký thông tin
trạng thái và giao dịch theo thời gian thực để chúng tôi có bản ghi về nó khi khắc phục
sự cố giao dịch. Chúng tôi sẽ sử dụng câu lệnh Print() như trước đây nhưng chúng tôi
sẽ ghi lại các giá trị chỉ báo, giá cả – bất kỳ thông tin nào hữu ích trong việc gỡ lỗi.
Chúng tôi cũng sẽ thêm một biến bên ngoài để bật và tắt đăng nhập. // Biến ngoài
extern bool Debug = true; 143 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN
NGHIỆP // Đặt ở gần cuối hàm start() if(Debug == true)
Print(StringConcatenate("Bid:",Bid," Ask:",Ask," MA:" ,MA, " MuaTicket:",BuyTicket,"
SellTicket:",SellTicket)); Đoạn mã trên sẽ ghi lại thông tin về giá và chỉ báo cũng như
nội dung của các biến BuyTicket và SellTicket. Nếu có bất kỳ câu hỏi nào về cách mở
giao dịch hoặc tại sao giao dịch không được mở, nhật ký tại thời điểm cụ thể đó sẽ
hiển thị trạng thái của tất cả các điều kiện giao dịch có liên quan. Bạn có thể bật và tắt
đăng nhập bằng biến ngoài Debug. Câu lệnh debug Print() phải được đặt ở gần cuối
hàm start(), sau tất cả các hàm giao dịch. Nếu bạn đang sử dụng bộ hẹn giờ và/hoặc
tính năng thực thi khi mở thanh, hãy đặt câu lệnh debug Print() bên trong khối bộ hẹn
giờ để nó chỉ chạy khi cần thiết. Nếu không, dòng gỡ lỗi sẽ in ra nhật ký sau mỗi tích
tắc, điều này có thể tạo ra một tệp nhật ký lớn. Sửa lỗi biên dịch Khi bạn biên dịch
Expert Advisor, trình biên dịch sẽ kiểm tra cú pháp đúng và đảm bảo rằng tất cả các
hàm và biến tùy chỉnh đã được khai báo đúng. Nếu bạn bỏ sót nội dung nào đó, trình
biên dịch sẽ dừng và mọi lỗi biên dịch sẽ xuất hiện trong tab Lỗi trong cửa sổ Hộp
công cụ. Khi gặp phải một danh sách dài các lỗi biên dịch, hãy luôn bắt đầu từ lỗi đầu
tiên. Bấm đúp vào lỗi trong danh sách và trình soạn thảo sẽ chuyển trực tiếp đến dòng
có lỗi. Sửa lỗi và biên dịch lại. Đôi khi một lỗi cú pháp đơn giản sẽ dẫn đến một số lỗi
không liên quan, mặc dù chỉ có lỗi đầu tiên là hợp lệ. Đây là danh sách các lỗi biên
dịch thường gặp và cách khắc phục: • Biến không được xác định – Bạn quên khai báo
một biến có kiểu dữ liệu. Nếu đó là biến toàn cục hoặc biến ngoài, hãy khai báo nó ở
đầu tệp. Nếu đó là biến cục bộ, hãy tìm lần xuất hiện đầu tiên và đặt phần khai báo
kiểu dữ liệu trước biến đó. Nếu không, hãy kiểm tra chính tả hoặc kiểu chữ
(hoa/thường) của tên biến. • Biến đã được xác định – Bạn đã khai báo cùng một biến
hai lần. Loại bỏ phần khai báo kiểu dữ liệu khỏi tất cả các khai báo biến trùng lặp. •
Hàm không được xác định – Nếu hàm được đề cập nằm trong tệp bao gồm hoặc thư
viện, hãy đảm bảo rằng lệnh #include hoặc #import được đặt ở đầu tệp và đúng. Nếu
không, hãy kiểm tra chính tả hoặc kiểu chữ của tên hàm và đảm bảo rằng nó tồn tại
trong tệp hiện tại hoặc trong tệp bao gồm hoặc thư viện có liên quan. 144
www.ZTCprep.com Mẹo và thủ thuật • Phép gán bất hợp pháp được sử dụng – Điều
này thường liên quan đến dấu bằng (=). Hãy nhớ rằng một dấu bằng duy nhất dành
cho phép gán biến và hai dấu bằng (==) là toán tử so sánh. Sửa toán tử gán thành toán
tử so sánh thích hợp. • Phép gán dự kiến – Điều này thường liên quan đến toán tử so
sánh "bằng" (==). Bạn đã sử dụng hai dấu bằng thay vì một trong phép gán biến. Sửa
toán tử thành một dấu bằng duy nhất. • Dấu ngoặc đơn bên phải không cân bằng –
Điều này thường xảy ra trong câu lệnh if khi sử dụng dấu ngoặc đơn lồng nhau. Đi đến
dòng được chỉ ra bởi lỗi đầu tiên và chèn dấu ngoặc đơn bên trái vào vị trí thích hợp. •
Dấu ngoặc trái không cân bằng – Đây là một dấu ngoặc đơn khó. Lỗi thường xảy ra ở
cuối dòng chương trình. Về cơ bản bạn đã quên dấu ngoặc đơn ở đâu đó. Kiểm tra kỹ
mã bạn đã chỉnh sửa gần đây và tìm dấu ngoặc đơn bên phải bị thiếu. Bạn có thể phải
nhận xét các dòng mã để xác định vấn đề. • Đếm tham số sai – Bạn có quá ít hoặc
quá nhiều đối số trong một hàm. Kiểm tra kỹ cú pháp hàm trong Tham chiếu MQL và
sửa các đối số. • Cần có dấu chấm phẩy – Có thể bạn đã quên đặt dấu chấm phẩy ở
cuối dòng. Đặt dấu chấm phẩy ở cuối dòng trước. Lưu ý rằng thiếu dấu chấm phẩy
cũng có thể gây ra bất kỳ lỗi nào ở trên, vì vậy hãy nhớ đặt các dấu chấm phẩy đó! 145
www.ZTCprep.com CHƯƠNG TRÌNH CỐ VẤN CHUYÊN GIA Chương 9 Các chỉ báo và
tập lệnh tùy chỉnh Không có cuốn sách nào về MQL sẽ hoàn chỉnh nếu không đề cập
đến các chỉ báo và tập lệnh tùy chỉnh. Các chỉ báo tích hợp trong MetaTrader khá hạn
chế, nhưng may mắn thay MQL cho phép các lập trình viên tạo các chỉ báo của riêng
họ. Nếu bạn đang tìm kiếm một chỉ báo phổ biến không có trong MT4 thì rất có thể ai
đó đã tạo một chỉ báo đó. Chương này sẽ là tổng quan cơ bản về việc tạo chỉ báo tùy
chỉnh. Hầu hết các chỉ báo đều sử dụng các công thức toán học phức tạp và đó là lĩnh
vực của các lập trình viên giàu kinh nghiệm hơn. Tuy nhiên, một chỉ báo không cần
phải phức tạp. Chúng ta sẽ tạo một chỉ báo tùy chỉnh trong chương này chỉ sử dụng
một vài dòng mã. Bộ đệm Bộ đệm là mảng lưu trữ các giá trị chỉ báo và phép tính. Một
chỉ báo tùy chỉnh có thể có tối đa 8 bộ đệm. Bộ đệm sử dụng các chỉ mục, giống như
mảng và có phạm vi từ 0 đến 7. Khi bạn gọi một chỉ báo tùy chỉnh trong chuyên gia tư
vấn bằng cách sử dụng hàm iCustom(), tham số tiếp theo trong hàm là bộ đệm chỉ
báo. Để tìm vùng đệm thích hợp cho dòng chỉ báo, bạn thường kiểm tra mã nguồn, nếu
có. Nếu mã nguồn được định dạng rõ ràng với các tên biến mô tả, bạn sẽ có thể xác
định bộ đệm thích hợp khá dễ dàng. Chúng ta sẽ đề cập đến cách đặt tên thích hợp
cho bộ đệm chỉ báo trong chương này. Tạo chỉ báo tùy chỉnh Hãy xây dựng một chỉ
báo tùy chỉnh bằng cách sử dụng hai chỉ báo MetaTrader tích hợp để tính toán các
đường của chúng tôi. Chúng ta sẽ xây dựng một chỉ báo Dải Bollinger đã được sửa
đổi. Dải Bollinger bao gồm 3 đường – đường trung tâm là đường trung bình động đơn
giản, cùng với đường trên và đường dưới có giá trị được xác định bằng độ lệch chuẩn.
Chúng ta có thể tạo chỉ báo Dải bollinger của riêng mình bằng cách sử dụng các chỉ
báo Trung bình trượt và Độ lệch chuẩn. Chúng tôi muốn tạo một chỉ báo sử dụng
đường trung bình động hàm mũ để tính toán các đường, trái ngược với đường trung
bình động đơn giản. 146 www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Chúng
tôi bắt đầu bằng cách sử dụng trình hướng dẫn để tạo tệp chỉ báo của mình. Chọn Mới
từ menu Tệp hoặc thanh công cụ để mở trình hướng dẫn và tạo chỉ báo tùy chỉnh. Điền
tên chỉ báo và thêm thông số nếu muốn. Trên trang cuối cùng, chúng tôi đã thêm ba
dòng chỉ báo cùng màu. Đây là kết quả của thuật sĩ. Hiện tại chúng ta đã bỏ qua hàm
start(): //+--------------------------------- --------------------------------+ //| EMA Bollinger.mq4 | //|
Andrew trẻ | //| http://www.easyexpertforex.com | //+-----------------------------------------------------
-- -------------------+ #bản quyền tài sản "Andrew Young" #link tài sản
"http://www.easyexpertforex.com" #property Indicator_chart_window #property
Indicator_buffers 3 #property Indicator_color1 DeepSkyBlue #property
Indicator_color2 DeepSkyBlue #property Indicator_color3 DeepSkyBlue //---- đệm gấp
đôi ExtMapBuffer1[]; nhân đôi ExtMapBuffer2[]; nhân đôi ExtMapBuffer3[]; //+--------------
----------------------------------------- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+---
---------------------------------------------------- -------------------+ int init() { //---- chỉ báo
SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,ExtMapBuffer1);
SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,ExtMapBuffer2);
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,ExtMapBuffer3); //---- return(0); } Hãy
chuyển sự chú ý của chúng ta tới các phần tử được in đậm. Khai báo #property đặt
tham số cho bộ đệm chỉ báo của chúng tôi. Thuộc tính Indicator_chart_window vẽ chỉ
báo của chúng tôi trong cửa sổ biểu đồ chính. Nếu chúng ta đang tạo một bộ dao
động và muốn vẽ chỉ báo trong một cửa sổ riêng biệt, thì thay vào đó, chúng ta sẽ sử
dụng thuộc tính Indicator_separate_window. Thuộc tính Indicator_buffers đặt số lượng
bộ đệm cho chỉ báo của chúng tôi. Trong trường hợp này chúng tôi đang sử dụng ba
bộ đệm. Thuộc tính Indicator_color đặt màu của cả ba dòng thành DeepSkyBlue. 147
www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN NGHIỆP Tiếp theo là các khai báo
cho mảng bộ đệm của chúng ta. Chúng tôi có ba bộ đệm có tên ExtMapBuffer(1-3).
Chúng tôi sẽ sớm thay đổi các mã định danh mảng này thành một cái gì đó mang tính
mô tả hơn. Hàm init() là nơi chúng ta đặt các thuộc tính cho bộ đệm chỉ báo của mình.
SetIndexBuffer() liên kết một mảng bộ đệm với một chỉ mục bộ đệm. Chỉ mục bộ đệm
là những gì chúng tôi đề cập đến khi đặt thuộc tính cho một dòng chỉ báo cũng như
khi chúng tôi gọi một dòng chỉ báo từ EA bằng cách sử dụng hàm iCustom(). Tham số
đầu tiên là một số nguyên từ 0 đến 7 và tham số thứ hai là tên của mảng đệm. Các
thuộc tính của bản vẽ Hàm SetIndexStyle() thiết lập loại đường để vẽ, cùng với các
thuộc tính của đường đó. Mỗi dòng chỉ báo sẽ có hàm SetIndexStyle() tương ứng. Đây
là cú pháp: void SetIndexStyle(int BufferIndex, int LineType, int LineStyle = EMPTY, int
LineWidth = EMPTY, color LineColor = CLR_NONE) • BufferIndex – Chỉ mục của bộ
đệm, từ 0 đến 7. • LineType – Đặt loại bộ đệm đường để vẽ. DRAW_LINE vẽ một
đường duy nhất, DRAW_HISTOGRAM vẽ biểu đồ dọc (xem ví dụ về OsMA hoặc Bộ tạo
dao động tuyệt vời), DRAW_ARROW vẽ một biểu tượng và DRAW_NONE không vẽ
đường nào. • LineStyle – Một tham số tùy chọn cho biết kiểu vẽ. Được sử dụng chủ
yếu cho các dòng loại DRAW_LINE. Theo mặc định, một đường liền nét được vẽ
(STYLE_SOLID). Bạn cũng có thể vẽ các đường đứt nét (STYLE_DASH) và chấm
(STYLE_DOT). • LineWidth – Một tham số tùy chọn cho biết chiều rộng của đường tính
bằng pixel. Giá trị mặc định là 1. • LineColor – Một tham số tùy chọn cho biết màu của
đường. Nếu bạn sử dụng trình hướng dẫn, màu sẽ được đặt bằng cách sử dụng khai
báo #property, nhưng bạn cũng có thể đặt màu ở đây. Nếu bạn đang sử dụng
DRAW_ARROW làm LineType, hàm SetArrow() cho phép bạn đặt ký hiệu phông chữ
Wingdings để vẽ trên biểu đồ. Tham số đầu tiên là chỉ mục bộ đệm và tham số thứ hai
là hằng số nguyên biểu thị ký hiệu cần vẽ. Các ký hiệu có thể được tìm thấy trong
Tham chiếu MQL trong Hằng số tiêu chuẩn – Mã mũi tên. Bạn có thể muốn thêm mô
tả cho các dòng chỉ báo sẽ được hiển thị trong chú giải công cụ hoặc trong cửa sổ dữ
liệu. Để thực hiện việc này, hãy sử dụng hàm SetIndexLabel(). Tham số đầu tiên là chỉ
mục bộ đệm và tham số thứ hai là mô tả văn bản. Chúng tôi sẽ sớm thêm những thứ
này vào chỉ báo của chúng tôi. 148 www.ZTCprep.com Các chỉ báo và tập lệnh tùy
chỉnh Nếu chỉ báo của bạn được vẽ trong một cửa sổ riêng biệt (chẳng hạn như bộ
dao động) và bạn muốn thêm các mức để biểu thị mức quá mua hoặc quá bán (chẳng
hạn như trong các chỉ báo Stochastic hoặc RSI) hoặc mức 0 (chẳng hạn như trong chỉ
báo CCI), bạn có thể sử dụng các hàm SetLevelStyle() và SetLevelValue(). Xem Tham
chiếu MQL trong Chỉ báo tùy chỉnh để biết thêm thông tin. Bạn cũng có thể muốn chỉ
định một tên chỉ báo ngắn gọn sẽ được hiển thị ở góc trên cùng bên trái của cửa sổ
chỉ báo. Sử dụng hàm IndicatorShortName() để đặt giá trị này. Tham số duy nhất là
một chuỗi văn bản sẽ xuất hiện ở góc trên bên trái của cửa sổ chỉ báo, cũng như trong
cửa sổ dữ liệu. Sử dụng tên bộ đệm mô tả Đây là mã chỉ báo được cập nhật của chúng
tôi. Lưu ý rằng chúng tôi đã đổi tên các mảng đệm để mang tính mô tả rõ hơn về chức
năng thực tế của chúng. Chúng tôi đã thay đổi tham số thứ hai của hàm
SetIndexBuffer() để phản ánh tên bộ đệm mới. Chúng tôi cũng đã thêm
SetIndexLabel() cho mỗi dòng để hiển thị tên mô tả trong Cửa sổ dữ liệu. //---- đệm đôi
EMA[]; gấp đôi UpperBand[]; gấp đôi LowerBand[]; //+-----------------------------------------------------
-- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+------------------------------------------
------------- -------------------+ int init() { //---- chỉ báo SetIndexStyle(0,DRAW_LINE);
SetIndexBuffer(0,EMA); SetIndexLabel(0,"EMA"); SetIndexStyle(1,DRAW_LINE);
SetIndexBuffer(1,UpperBand); SetIndexLabel(1,"UpperBand");
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,LowerBand);
SetIndexLabel(2,"LowerBand"); //---- return(0); } Chúng tôi đã đổi tên các mảng vùng
đệm từ tên mặc định (ExtMapBuffer) thành tên mang tính mô tả hơn. EMA[] sẽ là vùng
đệm của chúng tôi cho đường trung tâm và UpperBand[] và LowerBand[] sẽ lần lượt là
Ấ
dải trên và dải dưới. 149 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Các
hàm SetIndexBuffer() liên kết các chỉ mục bộ đệm với mảng bộ đệm của chúng ta.
EMA là 0, UpperBand là 1 và LowerBand là 2. Lưu ý rằng các dấu ngoặc nhọn bị loại
khỏi tên định danh mảng cho tham số SetIndexBuffer() thứ hai. Các hàm
SetIndexLabel() đặt tên mô tả cho từng vùng đệm chỉ báo. Trong trường hợp này, tên
dòng giống với tên định danh của chúng tôi. Chúng sẽ xuất hiện trên chú giải công cụ
chuột cũng như trong Cửa sổ dữ liệu. Nếu một lập trình viên khác quyết định sử dụng
chỉ báo này trong chuyên gia cố vấn, thì định dạng ở trên sẽ làm rõ chính xác chỉ mục
bộ đệm chỉ báo nào họ nên sử dụng cho mỗi dòng. Hàm start() của chỉ báo Trình
hướng dẫn chỉ chèn một biểu thức vào hàm start(): int count_bars =
IndicatorCounted(); IndicatorCounted() trả về số thanh trên biểu đồ mà chỉ báo đã tính
toán. Khi EA được khởi động lần đầu tiên, giá trị này sẽ là 0. Chỉ báo sẽ được tính cho
mọi thanh trên biểu đồ. Trên các thanh tiếp theo, chúng ta sẽ kiểm tra hàm
IndicatorCounted() để xem có bao nhiêu thanh đã được tính toán, từ đó chúng ta sẽ
biết chính xác có bao nhiêu thanh mới cần tính toán. Việc tính toán chỉ báo của chúng
tôi sẽ diễn ra bên trong vòng lặp for. Điểm bắt đầu sẽ là thanh chưa được tính toán
đầu tiên và điểm kết thúc sẽ là thanh hiện tại. Chúng ta sẽ so sánh giá trị của
IndicatorCounted() với biến Bars được xác định trước, biến này trả về số lượng thanh
trên biểu đồ hiện tại. Điều này sẽ xác định điểm khởi đầu của chúng tôi. Đây là mã cho
vòng lặp for: int count_bars = IndicatorCounted(); if(counted_bars > 0) count_bars--; int
CalculateBars = Thanh - count_bars; for(int Count = CalculateBars; Count >= 0; Count-
-) { // Tính toán chỉ báo } Câu lệnh if đầu tiên sẽ giảm giá trị của count_bars đi 1 khi
tính toán các thanh mới. Chúng tôi sẽ luôn tính toán ít nhất hai thanh trước đó. Điều
này là do điều kiện có thể không tính được dấu tích cuối cùng của thanh trong một số
trường hợp. Tiếp theo, chúng tôi xác định số lượng thanh cần tính toán bằng cách trừ
count_bars khỏi biến Bars được xác định trước. Điều này được lưu trữ trong biến
CalculateBars. 150 www.ZTCprep.com Chỉ báo và Tập lệnh Tùy chỉnh Trong vòng lặp
for của chúng tôi, biến tăng Count được đặt thành giá trị của CalculateBars, điều kiện
để kết thúc là khi Count nhỏ hơn 0 và biến Count được giảm dần sau mỗi lần lặp. Điều
này sẽ tính toán từng thanh trên biểu đồ từ trái sang phải. Đây là mã để tính Dải
Bollinger của chúng tôi. Chúng ta sẽ khai báo biến ngoài BandsPeriod ở đầu tệp. Vòng
lặp for là vòng lặp chúng ta đã tạo ở trên: // Các tham số bên ngoài extern int
BandsPeriod = 20; // hàm start() for(int Count = CalculateBars; Count >= 0; Count--) {
EMA[Count] = iMA(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); UpperBand[Count] = EMA[Count]
+ StdDev; LowerBand[Count] = EMA[Count] - StdDev; } Đầu tiên, chúng ta gọi chỉ báo
Trung bình trượt tích hợp bằng hàm iMA() và gán giá trị trả về cho EMA[Count]. Lưu ý
rằng chỉ mục mảng và tham số Shift cho chỉ báo trung bình động đều sử dụng giá trị
Đếm hiện tại. Tiếp theo, chúng tôi gọi chỉ báo Độ lệch chuẩn bằng iStdDev(). Để tính
dải trên, tất cả những gì chúng ta cần làm là cộng độ lệch chuẩn vào đường trung bình
động. Điều này được lưu trữ trong mảng đệm UpperBand[]. Để tính LowerBand[],
chúng tôi trừ đi độ lệch chuẩn khỏi mức trung bình động. Hãy mở rộng chỉ báo của
chúng tôi thêm một chút bằng cách cung cấp cho nó đầy đủ các cài đặt. Chúng tôi sẽ
thêm cài đặt để điều chỉnh dịch chuyển kỳ hạn, phương pháp trung bình động và các
tham số giá được áp dụng cũng như điều chỉnh độ lệch chuẩn: // Tham số bên ngoài
extern int BandsPeriod = 20; extern int BandsShift = 0; extern int BandsMethod = 1;
extern int BandsPrice = 0; extern int Độ lệch = 1; 151 www.ZTCprep.com LẬP TRÌNH
TƯ VẤN CHUYÊN NGHIỆP // start() function for(int Count = CalculateBars; Count >= 0;
Count--) { EMA[Count] =
iMA(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice ,Đếm); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice,Count);
UpperBand[Count] = EMA[Count] + (StdDev * Độ lệch); LowerBand[Count] =
EMA[Count] - (StdDev * Độ lệch); } Chúng tôi đã thêm các biến bên ngoài để điều chỉnh
các tham số còn lại cho hàm iMA() và iStdDev(). Chúng tôi cũng đã thêm một tham số
để điều chỉnh số lượng độ lệch chuẩn. Để tính toán điều này, chúng tôi chỉ cần nhân
StdDev với Độ lệch. Bây giờ chúng ta có chỉ báo Dải bollinger có thể điều chỉnh hoàn
toàn, linh hoạt hơn chỉ báo MetaTrader tiêu chuẩn. Mã đầy đủ được liệt kê trong Phụ
lục E. Bạn có thể làm được nhiều việc hơn với các chỉ báo tùy chỉnh thay vì chỉ tính
toán lại các chỉ báo tích hợp. Tùy thuộc vào trình độ kiến thức toán học của bạn, bạn
có thể mã hóa các chỉ báo không có trong MetaTrader hoặc thậm chí tạo chỉ báo của
riêng bạn. Bạn cũng có thể vẽ và thao tác các đối tượng. Nếu bạn muốn tìm hiểu thêm
về cách tạo chỉ báo tùy chỉnh, hãy xem chủ đề Tham khảo MQL Chỉ báo tùy chỉnh,
Hàm đối tượng và Toán & Lượng giác. Tập lệnh Tập lệnh là một chương trình MQL chỉ
chạy một lần khi nó được đính kèm lần đầu tiên vào biểu đồ. Tập lệnh có thể được sử
dụng để tự động hóa một loạt hành động giao dịch, chẳng hạn như đóng tất cả các
lệnh trên biểu đồ hoặc gửi lệnh chờ xử lý. Một số tập lệnh, chẳng hạn như tập lệnh
Period_converter đi kèm với MetaTrader, có thể vẽ lại biểu đồ dựa trên khoảng thời
gian tùy chỉnh. Tệp mã nguồn tập lệnh phải có chỉ thị thuộc tính show_confirm hoặc
show_inputs. Thuộc tính show_confirm nhắc người dùng xác nhận hoạt động của tập
lệnh, trong khi show_inputs hiển thị hộp thoại thuộc tính tập lệnh. #property
show_confirm // hiển thị hộp thoại xác nhận #property show_inputs // hiển thị hộp
thoại thuộc tính Nếu tập lệnh của bạn có các tham số cần điều chỉnh, hãy sử dụng
thuộc tính show_inputs. Nếu không, hãy sử dụng show_confirm. 152
www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Cũng giống như các chỉ báo và
cố vấn chuyên gia, các tập lệnh sử dụng các hàm init(), deinit() và start(). Hãy nhớ
rằng mỗi hàm sẽ chỉ được chạy một lần – init() và start() khi tập lệnh được khởi động
và deinit() khi tập lệnh bị xóa. Bạn có thể đính kèm một tập lệnh vào biểu đồ tại một
thời điểm. MetaTrader đi kèm với một số tập lệnh mẫu. Tất cả các tập lệnh được lưu
trong thư mục \experts\scripts. 153 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục A Cố vấn chuyên gia đơn giản Đây là chuyên gia cố vấn đơn
giản từ chương 2. #property bản quyền "Andrew Young" // Biến ngoài extern double
LotSize = 0.1; StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern
int Trượt = 5; extern int MagicNumber = 123; extern int FastMAPeriod = 10; extern int
SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int BánTicket; điểm sử dụng gấp
đôi; int UseTrượt trượt; // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol());
UseSlippage = GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường
trung bình động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi
SlowMA = iMA(NULL,0,SlowMAPeriod,0,0,0,0); 154 www.ZTCprep.com Phụ lục A //
Lệnh mua if(FastMA > SlowMA && BuyTicket == 0) {
OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0 &&
SellTicket > 0) { double CloseLots = OrderLots(); gấp đôi Giá đóng cửa = Hỏi; bool Đã
đóng = OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } double
OpenPrice = Hỏi; // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss
= OpenPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit =
OpenPrice + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Order",MagicNumber,0,Green); BánTicket = 0; } //
Lệnh bán if(FastMA < SlowMA && SellTicket == 0) {
OrderSelect(BuyTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0 && BuyTicket >
0) { CloseLots = OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } OpenPrice = Giá thầu;
if(StopLoss > 0) double SellStopLoss = OpenPrice + (StopLoss * UsePoint);
if(TakeProfit > 0) double SellTakeProfit = OpenPrice - (TakeProfit * UsePoint);
SellTicket = OrderSend(Symbol(),OP_SELL,LotSize,OpenPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Sell Order",MagicNumber,0,Red); MuaVé = 0; } 155
www.ZTCprep.com CHUYÊN GIA LẬP TRÌNH TƯ VẤN return(0); } // Hàm điểm Pip
nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits = MarketInfo(Tiền tệ,MODE_DIGITS);
if(CalcDigits == 2 || CalcDigits == 3) gấp đôi CalcPoint = 0,01; khác nếu(CalcDigits == 4
|| CalcDigits == 5) CalcPoint = 0,0001; return(CalcPoint); } // Lấy hàm trượt giá int
GetSlippage(string Money, int SlippagePips) { int CalcDigits =
MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 4) double
CalcSlippage = SlippagePips; else if(CalcDigits == 3 || CalcDigits == 5) CalcSlippage =
SlippagePips * 10; return(CalcSlippage); } Chuyên gia cố vấn đơn giản với các lệnh
đang chờ xử lý Đây là chuyên gia cố vấn đơn giản sử dụng các lệnh dừng đang chờ xử
lý: #property Copyright "Andrew Young" // Biến bên ngoài extern double LotSize = 0.1;
StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern int
PendingPips = 10; extern int Trượt = 5; extern int MagicNumber = 123; extern int
FastMAPeriod = 10; extern int SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int
BánTicket; điểm sử dụng gấp đôi; int UseTrượt trượt; 156 www.ZTCprep.com Phụ lục
A // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol()); UseSlippage =
GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường trung bình
động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,0); // Lệnh mua if(FastMA > SlowMA && BuyTicket ==
0) { OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0
&& SellTicket > 0 && OrderType() == OP_SELL) { double CloseLots = OrderLots(); gấp
đôi Giá đóng cửa = Hỏi; bool Đã đóng =
OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } // Xóa đơn hàng khác
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_SELLSTOP) { bool
Đã xóa = OrderDelete(SellTicket,Red); } double PendingPrice = Cao[0] + (PendingPips *
UsePoint); // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss =
PendingPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit = Đang
chờ xử lý + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Stop Order",MagicNumber,0,Green); BánTicket = 0; }
157 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA // Bán lệnh if(FastMA <
SlowMA && SellTicket == 0) { OrderSelect(BuyTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0 && BuyTicket > 0 && OrderType() == OP_BUY) { CloseLots =
OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } else
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_BUYSTOP) { Đã xóa
= OrderDelete(SellTicket,Red); } Giá đang chờ xử lý = Thấp[0] - (PendingPips *
UsePoint); if(StopLoss > 0) double SellStopLoss = Giá đang chờ xử lý + (StopLoss *
UsePoint); if(TakeProfit > 0) double SellTakeProfit = Đang chờ xử lý - (TakeProfit *
UsePoint); SellTicket =
OrderSend(Symbol(),OP_SELLSTOP,LotSize,PendingPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Lệnh dừng bán",MagicNumber,0,Red); MuaVé = 0; } trả
về(0); } // Hàm điểm Pip nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits =
MarketInfo(Tiền tệ,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 3) gấp đôi
CalcPoint = 0,01; khác nếu(CalcDigits == 4 || CalcDigits == 5) CalcPoint = 0,0001;
return(CalcPoint); } // Lấy hàm trượt giá int GetSlippage(string Money, int
SlippagePips) { int CalcDigits = MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2
|| CalcDigits == 4) double CalcSlippage = SlippagePips; else if(CalcDigits == 3 ||
CalcDigits == 5) CalcSlippage = SlippagePips * 10; return(CalcSlippage); } 158
www.ZTCprep.com Phụ lục A 159 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục B Cố vấn chuyên gia nâng cao Đây là chuyên gia cố vấn với
các tính năng nâng cao từ chương 3. #property Copyright "Andrew Young" #include
"điểm dừng không hợp lệ" mỗi khi chúng tôi đặt lệnh mua. Chúng tôi biết rằng lỗi 130
có nghĩa là lệnh dừng lỗ hoặc chốt lãi không chính xác. Bạn có thể xác định được lỗi
không? if(Close[0] > MA && BuyTicket == 0) { double Openprice = Hỏi; gấp đôi
BuyStopLoss = OpenPrice + (StopLoss * UsePoint); gấp đôi BuyTakeProfit = OpenPrice
+ (TakeProfit * UsePoint); BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,Openprice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Mua đơn hàng",MagicNumber,0,Green); BánTicket = 0; }
Chúng ta sẽ sử dụng hàm Print() để xác minh các tham số đang được truyền cho hàm
OrderSend(). Chúng ta sẽ tập trung vào giá mở lệnh, mức dừng lỗ và chốt lời.
Print("Giá:"+OpenPrice+" Dừng:"+BuyStopLoss+" Lợi nhuận:"+BuyTakeProfit); Đây là
kết quả khi chúng tôi chạy EA trong trình thử nghiệm chiến lược. Giả sử mức dừng lỗ
và chốt lời là 50 pips: 11:52:12 2009.11.02 02:00 Ví dụ EURUSD,H1: Lỗi gửi lệnh 130
11:52:12 2009.11.02 02:00 Ví dụ EURUSD,H1: Giá:1.47340000 Dừng:1.47840000 Lợi
nhuận:1.47840000 Chúng tôi biết rằng mức dừng lỗ phải thấp hơn giá mở cửa cho
một lệnh mua. Ở đây, nó cao hơn giá. Trên thực tế, nó có cùng mức giá với mức chốt
lời. Xem nhanh mã của chúng tôi và chúng tôi nhận ra rằng chúng tôi đã vô tình chèn
dấu cộng vào phương trình dừng lỗ mua. Đây là mã chính xác: 142 www.ZTCprep.com
Mẹo và thủ thuật nhân đôi BuyStopLoss = OpenPrice - (StopLoss * UsePoint); Nếu bạn
nhận được thông báo lỗi khi cố gắng đặt, đóng hoặc sửa đổi đơn hàng, hãy tập trung
nỗ lực vào vấn đề được chỉ ra bởi thông báo lỗi. Dưới đây là một số thông báo lỗi phổ
biến nhất do lỗi lập trình gây ra: • Lỗi 129: Giá không hợp lệ – Giá mở cửa không hợp
lệ. Đối với các lệnh thị trường, hãy đảm bảo giá Mua hoặc Bán hiện tại đang được
thông qua, tùy theo loại lệnh. Đối với các lệnh đang chờ xử lý, hãy đảm bảo giá cao
hơn hoặc thấp hơn giá hiện tại, theo yêu cầu của loại lệnh. Đồng thời kiểm tra xem giá
lệnh chờ xử lý có quá gần với giá hiện tại hay không (tức là nằm trong mức dừng). • Lỗi
130: Điểm dừng không hợp lệ – Giá dừng lỗ hoặc chốt lãi không chính xác. Kiểm tra
xem giá dừng lỗ và chốt lời được đặt ở trên hay dưới giá hiện tại, tùy thuộc vào loại
lệnh là mua hay bán. Đồng thời kiểm tra xem giá dừng lỗ hoặc chốt lãi không quá gần
với giá hiện tại (tức là nằm trong mức dừng). • Lỗi 131: Khối lượng giao dịch không
hợp lệ – Kích thước lô không chính xác. Đảm bảo rằng kích thước lô không vượt quá
mức tối thiểu hoặc tối đa của nhà môi giới và kích thước lô được chuẩn hóa thành giá
trị bước chính xác (0,1 hoặc 0,01 trên hầu hết các nhà môi giới). Bạn có thể tìm thấy
mô tả của tất cả các thông báo lỗi trong Tham chiếu MQL trong Hằng số tiêu chuẩn –
Mã lỗi. Nếu bạn cần hỗ trợ thêm về lỗi mà bạn đang gặp phải, hãy kiểm tra các diễn
đàn tại MQL4.com. Khắc phục sự cố các lỗi giao dịch không liên tục Trong khi hầu hết
các lỗi nghiêm trọng có thể được tìm thấy đơn giản bằng cách kiểm tra lại, những lỗi
khác sẽ chỉ xảy ra trong quá trình giao dịch theo thời gian thực. Lỗi logic có thể dẫn
đến giao dịch không được đặt chính xác và bạn có thể phải mất một chút nỗ lực để
xác định những lỗi này. Nếu có giao dịch được đặt không chính xác trong quá trình
giao dịch demo hoặc giao dịch trực tiếp, chúng tôi cần nhiều thông tin cần thiết để
khắc phục sự cố. Chúng tôi sẽ thêm một tính năng tùy chọn để ghi nhật ký thông tin
trạng thái và giao dịch theo thời gian thực để chúng tôi có bản ghi về nó khi khắc phục
sự cố giao dịch. Chúng tôi sẽ sử dụng câu lệnh Print() như trước đây nhưng chúng tôi
sẽ ghi lại các giá trị chỉ báo, giá cả – bất kỳ thông tin nào hữu ích trong việc gỡ lỗi.
Chúng tôi cũng sẽ thêm một biến bên ngoài để bật và tắt đăng nhập. // Biến ngoài
extern bool Debug = true; 143 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN
NGHIỆP // Đặt ở gần cuối hàm start() if(Debug == true)
Print(StringConcatenate("Bid:",Bid," Ask:",Ask," MA:" ,MA, " MuaTicket:",BuyTicket,"
SellTicket:",SellTicket)); Đoạn mã trên sẽ ghi lại thông tin về giá và chỉ báo cũng như
nội dung của các biến BuyTicket và SellTicket. Nếu có bất kỳ câu hỏi nào về cách mở
giao dịch hoặc tại sao giao dịch không được mở, nhật ký tại thời điểm cụ thể đó sẽ
hiển thị trạng thái của tất cả các điều kiện giao dịch có liên quan. Bạn có thể bật và tắt
đăng nhập bằng biến ngoài Debug. Câu lệnh debug Print() phải được đặt ở gần cuối
hàm start(), sau tất cả các hàm giao dịch. Nếu bạn đang sử dụng bộ hẹn giờ và/hoặc
tính năng thực thi khi mở thanh, hãy đặt câu lệnh debug Print() bên trong khối bộ hẹn
giờ để nó chỉ chạy khi cần thiết. Nếu không, dòng gỡ lỗi sẽ in ra nhật ký sau mỗi tích
tắc, điều này có thể tạo ra một tệp nhật ký lớn. Sửa lỗi biên dịch Khi bạn biên dịch
Expert Advisor, trình biên dịch sẽ kiểm tra cú pháp đúng và đảm bảo rằng tất cả các
hàm và biến tùy chỉnh đã được khai báo đúng. Nếu bạn bỏ sót nội dung nào đó, trình
biên dịch sẽ dừng và mọi lỗi biên dịch sẽ xuất hiện trong tab Lỗi trong cửa sổ Hộp
công cụ. Khi gặp phải một danh sách dài các lỗi biên dịch, hãy luôn bắt đầu từ lỗi đầu
tiên. Bấm đúp vào lỗi trong danh sách và trình soạn thảo sẽ chuyển trực tiếp đến dòng
có lỗi. Sửa lỗi và biên dịch lại. Đôi khi một lỗi cú pháp đơn giản sẽ dẫn đến một số lỗi
không liên quan, mặc dù chỉ có lỗi đầu tiên là hợp lệ. Đây là danh sách các lỗi biên
dịch thường gặp và cách khắc phục: • Biến không được xác định – Bạn quên khai báo
một biến có kiểu dữ liệu. Nếu đó là biến toàn cục hoặc biến ngoài, hãy khai báo nó ở
đầu tệp. Nếu đó là biến cục bộ, hãy tìm lần xuất hiện đầu tiên và đặt phần khai báo
kiểu dữ liệu trước biến đó. Nếu không, hãy kiểm tra chính tả hoặc kiểu chữ
(hoa/thường) của tên biến. • Biến đã được xác định – Bạn đã khai báo cùng một biến
hai lần. Loại bỏ phần khai báo kiểu dữ liệu khỏi tất cả các khai báo biến trùng lặp. •
Hàm không được xác định – Nếu hàm được đề cập nằm trong tệp bao gồm hoặc thư
viện, hãy đảm bảo rằng lệnh #include hoặc #import được đặt ở đầu tệp và đúng. Nếu
không, hãy kiểm tra chính tả hoặc kiểu chữ của tên hàm và đảm bảo rằng nó tồn tại
trong tệp hiện tại hoặc trong tệp bao gồm hoặc thư viện có liên quan. 144
www.ZTCprep.com Mẹo và thủ thuật • Phép gán bất hợp pháp được sử dụng – Điều
này thường liên quan đến dấu bằng (=). Hãy nhớ rằng một dấu bằng duy nhất dành
cho phép gán biến và hai dấu bằng (==) là toán tử so sánh. Sửa toán tử gán thành toán
tử so sánh thích hợp. • Phép gán dự kiến – Điều này thường liên quan đến toán tử so
sánh "bằng" (==). Bạn đã sử dụng hai dấu bằng thay vì một trong phép gán biến. Sửa
toán tử thành một dấu bằng duy nhất. • Dấu ngoặc đơn bên phải không cân bằng –
Điều này thường xảy ra trong câu lệnh if khi sử dụng dấu ngoặc đơn lồng nhau. Đi đến
dòng được chỉ ra bởi lỗi đầu tiên và chèn dấu ngoặc đơn bên trái vào vị trí thích hợp. •
Dấu ngoặc trái không cân bằng – Đây là một dấu ngoặc đơn khó. Lỗi thường xảy ra ở
cuối dòng chương trình. Về cơ bản bạn đã quên dấu ngoặc đơn ở đâu đó. Kiểm tra kỹ
mã bạn đã chỉnh sửa gần đây và tìm dấu ngoặc đơn bên phải bị thiếu. Bạn có thể phải
nhận xét các dòng mã để xác định vấn đề. • Đếm tham số sai – Bạn có quá ít hoặc
quá nhiều đối số trong một hàm. Kiểm tra kỹ cú pháp hàm trong Tham chiếu MQL và
sửa các đối số. • Cần có dấu chấm phẩy – Có thể bạn đã quên đặt dấu chấm phẩy ở
cuối dòng. Đặt dấu chấm phẩy ở cuối dòng trước. Lưu ý rằng thiếu dấu chấm phẩy
cũng có thể gây ra bất kỳ lỗi nào ở trên, vì vậy hãy nhớ đặt các dấu chấm phẩy đó! 145
www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA Chương 9 Các chỉ báo và tập
lệnh tùy chỉnh Không có cuốn sách nào về MQL sẽ hoàn chỉnh nếu không đề cập đến
các chỉ báo và tập lệnh tùy chỉnh. Các chỉ báo tích hợp trong MetaTrader khá hạn chế,
nhưng may mắn thay MQL cho phép các lập trình viên tạo các chỉ báo của riêng họ.
Nếu bạn đang tìm kiếm một chỉ báo phổ biến không có trong MT4 thì rất có thể ai đó
đã tạo một chỉ báo đó. Chương này sẽ là tổng quan cơ bản về việc tạo chỉ báo tùy
chỉnh. Hầu hết các chỉ báo đều sử dụng các công thức toán học phức tạp và đó là lĩnh
vực của các lập trình viên giàu kinh nghiệm hơn. Tuy nhiên, một chỉ báo không cần
phải phức tạp. Chúng ta sẽ tạo một chỉ báo tùy chỉnh trong chương này chỉ sử dụng
một vài dòng mã. Bộ đệm Bộ đệm là mảng lưu trữ các giá trị chỉ báo và phép tính. Một
chỉ báo tùy chỉnh có thể có tối đa 8 bộ đệm. Bộ đệm sử dụng các chỉ mục, giống như
mảng và có phạm vi từ 0 đến 7. Khi bạn gọi một chỉ báo tùy chỉnh trong chuyên gia tư
vấn bằng cách sử dụng hàm iCustom(), tham số tiếp theo trong hàm là bộ đệm chỉ
báo. Để tìm vùng đệm thích hợp cho dòng chỉ báo, bạn thường kiểm tra mã nguồn, nếu
có. Nếu mã nguồn được định dạng rõ ràng với các tên biến mô tả, bạn sẽ có thể xác
định bộ đệm thích hợp khá dễ dàng. Chúng ta sẽ đề cập đến cách đặt tên thích hợp
cho bộ đệm chỉ báo trong chương này. Tạo chỉ báo tùy chỉnh Hãy xây dựng một chỉ
báo tùy chỉnh bằng cách sử dụng hai chỉ báo MetaTrader tích hợp để tính toán các
đường của chúng tôi. Chúng ta sẽ xây dựng một chỉ báo Dải Bollinger đã được sửa
đổi. Dải Bollinger bao gồm 3 đường – đường trung tâm là đường trung bình động đơn
giản, cùng với đường trên và đường dưới có giá trị được xác định bằng độ lệch chuẩn.
Chúng ta có thể tạo chỉ báo Dải bollinger của riêng mình bằng cách sử dụng các chỉ
báo Trung bình trượt và Độ lệch chuẩn. Chúng tôi muốn tạo một chỉ báo sử dụng
đường trung bình động hàm mũ để tính toán các đường, trái ngược với đường trung
bình động đơn giản. 146 www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Chúng
tôi bắt đầu bằng cách sử dụng trình hướng dẫn để tạo tệp chỉ báo của mình. Chọn Mới
từ menu Tệp hoặc thanh công cụ để mở trình hướng dẫn và tạo chỉ báo tùy chỉnh. Điền
tên chỉ báo và thêm thông số nếu muốn. Trên trang cuối cùng, chúng tôi đã thêm ba
dòng chỉ báo cùng màu. Đây là kết quả của thuật sĩ. Hiện tại chúng ta đã bỏ qua hàm
start(): //+--------------------------------- --------------------------------+ //| EMA Bollinger.mq4 | //|
Andrew trẻ | //| http://www.easyexpertforex.com | //+-----------------------------------------------------
-- -------------------+ #bản quyền tài sản "Andrew Young" #link tài sản
"http://www.easyexpertforex.com" #property Indicator_chart_window #property
Indicator_buffers 3 #property Indicator_color1 DeepSkyBlue #property
Indicator_color2 DeepSkyBlue #property Indicator_color3 DeepSkyBlue //---- đệm gấp
đôi ExtMapBuffer1[]; nhân đôi ExtMapBuffer2[]; nhân đôi ExtMapBuffer3[]; //+--------------
----------------------------------------- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+---
---------------------------------------------------- -------------------+ int init() { //---- chỉ báo
SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,ExtMapBuffer1);
SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,ExtMapBuffer2);
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,ExtMapBuffer3); //---- return(0); } Hãy
chuyển sự chú ý của chúng ta tới các phần tử được in đậm. Khai báo #property đặt
tham số cho bộ đệm chỉ báo của chúng tôi. Thuộc tính Indicator_chart_window vẽ chỉ
báo của chúng tôi trong cửa sổ biểu đồ chính. Nếu chúng ta đang tạo một bộ dao
động và muốn vẽ chỉ báo trong một cửa sổ riêng biệt, thì thay vào đó, chúng ta sẽ sử
dụng thuộc tính Indicator_separate_window. Thuộc tính Indicator_buffers đặt số lượng
bộ đệm cho chỉ báo của chúng tôi. Trong trường hợp này chúng tôi đang sử dụng ba
bộ đệm. Thuộc tính Indicator_color đặt màu của cả ba dòng thành DeepSkyBlue. 147
Ấ
www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN NGHIỆP Tiếp theo là các khai báo
cho mảng bộ đệm của chúng ta. Chúng tôi có ba bộ đệm có tên ExtMapBuffer(1-3).
Chúng tôi sẽ sớm thay đổi các mã định danh mảng này thành một cái gì đó mang tính
mô tả hơn. Hàm init() là nơi chúng ta đặt các thuộc tính cho bộ đệm chỉ báo của mình.
SetIndexBuffer() liên kết một mảng bộ đệm với một chỉ mục bộ đệm. Chỉ mục bộ đệm
là những gì chúng tôi đề cập đến khi đặt thuộc tính cho một dòng chỉ báo cũng như
khi chúng tôi gọi một dòng chỉ báo từ EA bằng cách sử dụng hàm iCustom(). Tham số
đầu tiên là một số nguyên từ 0 đến 7 và tham số thứ hai là tên của mảng đệm. Các
thuộc tính của bản vẽ Hàm SetIndexStyle() thiết lập loại đường để vẽ, cùng với các
thuộc tính của đường đó. Mỗi dòng chỉ báo sẽ có hàm SetIndexStyle() tương ứng. Đây
là cú pháp: void SetIndexStyle(int BufferIndex, int LineType, int LineStyle = EMPTY, int
LineWidth = EMPTY, color LineColor = CLR_NONE) • BufferIndex – Chỉ mục của bộ
đệm, từ 0 đến 7. • LineType – Đặt loại bộ đệm đường để vẽ. DRAW_LINE vẽ một
đường duy nhất, DRAW_HISTOGRAM vẽ biểu đồ dọc (xem ví dụ về OsMA hoặc Bộ tạo
dao động tuyệt vời), DRAW_ARROW vẽ một biểu tượng và DRAW_NONE không vẽ
đường nào. • LineStyle – Một tham số tùy chọn cho biết kiểu vẽ. Được sử dụng chủ
yếu cho các dòng loại DRAW_LINE. Theo mặc định, một đường liền nét được vẽ
(STYLE_SOLID). Bạn cũng có thể vẽ các đường đứt nét (STYLE_DASH) và chấm
(STYLE_DOT). • LineWidth – Một tham số tùy chọn cho biết chiều rộng của đường tính
bằng pixel. Giá trị mặc định là 1. • LineColor – Một tham số tùy chọn cho biết màu của
đường. Nếu bạn sử dụng trình hướng dẫn, màu sẽ được đặt bằng cách sử dụng khai
báo #property, nhưng bạn cũng có thể đặt màu ở đây. Nếu bạn đang sử dụng
DRAW_ARROW làm LineType, hàm SetArrow() cho phép bạn đặt ký hiệu phông chữ
Wingdings để vẽ trên biểu đồ. Tham số đầu tiên là chỉ mục bộ đệm và tham số thứ hai
là hằng số nguyên biểu thị ký hiệu cần vẽ. Các ký hiệu có thể được tìm thấy trong
Tham chiếu MQL trong Hằng số tiêu chuẩn – Mã mũi tên. Bạn có thể muốn thêm mô
tả cho các dòng chỉ báo sẽ được hiển thị trong chú giải công cụ hoặc trong cửa sổ dữ
liệu. Để thực hiện việc này, hãy sử dụng hàm SetIndexLabel(). Tham số đầu tiên là chỉ
mục bộ đệm và tham số thứ hai là mô tả văn bản. Chúng tôi sẽ sớm thêm những thứ
này vào chỉ báo của chúng tôi. 148 www.ZTCprep.com Các chỉ báo và tập lệnh tùy
chỉnh Nếu chỉ báo của bạn được vẽ trong một cửa sổ riêng biệt (chẳng hạn như bộ
dao động) và bạn muốn thêm các mức để biểu thị mức quá mua hoặc quá bán (chẳng
hạn như trong các chỉ báo Stochastic hoặc RSI) hoặc mức 0 (chẳng hạn như trong chỉ
báo CCI), bạn có thể sử dụng các hàm SetLevelStyle() và SetLevelValue(). Xem Tham
chiếu MQL trong Chỉ báo tùy chỉnh để biết thêm thông tin. Bạn cũng có thể muốn chỉ
định một tên chỉ báo ngắn gọn sẽ được hiển thị ở góc trên cùng bên trái của cửa sổ
chỉ báo. Sử dụng hàm IndicatorShortName() để đặt giá trị này. Tham số duy nhất là
một chuỗi văn bản sẽ xuất hiện ở góc trên bên trái của cửa sổ chỉ báo, cũng như trong
cửa sổ dữ liệu. Sử dụng tên bộ đệm mô tả Đây là mã chỉ báo được cập nhật của chúng
tôi. Lưu ý rằng chúng tôi đã đổi tên các mảng đệm để mang tính mô tả rõ hơn về chức
năng thực tế của chúng. Chúng tôi đã thay đổi tham số thứ hai của hàm
SetIndexBuffer() để phản ánh tên bộ đệm mới. Chúng tôi cũng đã thêm
SetIndexLabel() cho mỗi dòng để hiển thị tên mô tả trong Cửa sổ dữ liệu. //---- đệm đôi
EMA[]; gấp đôi UpperBand[]; gấp đôi LowerBand[]; //+-----------------------------------------------------
-- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+------------------------------------------
------------- -------------------+ int init() { //---- chỉ báo SetIndexStyle(0,DRAW_LINE);
SetIndexBuffer(0,EMA); SetIndexLabel(0,"EMA"); SetIndexStyle(1,DRAW_LINE);
SetIndexBuffer(1,UpperBand); SetIndexLabel(1,"UpperBand");
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,LowerBand);
SetIndexLabel(2,"LowerBand"); //---- return(0); } Chúng tôi đã đổi tên các mảng vùng
đệm từ tên mặc định (ExtMapBuffer) thành tên mang tính mô tả hơn. EMA[] sẽ là vùng
đệm của chúng tôi cho đường trung tâm và UpperBand[] và LowerBand[] sẽ lần lượt là
dải trên và dải dưới. 149 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Các
hàm SetIndexBuffer() liên kết các chỉ mục bộ đệm với mảng bộ đệm của chúng ta.
EMA là 0, UpperBand là 1 và LowerBand là 2. Lưu ý rằng các dấu ngoặc nhọn bị loại
khỏi tên định danh mảng cho tham số SetIndexBuffer() thứ hai. Các hàm
SetIndexLabel() đặt tên mô tả cho từng vùng đệm chỉ báo. Trong trường hợp này, tên
dòng giống với tên định danh của chúng tôi. Chúng sẽ xuất hiện trên chú giải công cụ
chuột cũng như trong Cửa sổ dữ liệu. Nếu một lập trình viên khác quyết định sử dụng
chỉ báo này trong chuyên gia cố vấn, thì định dạng ở trên sẽ làm rõ chính xác chỉ mục
bộ đệm chỉ báo nào họ nên sử dụng cho mỗi dòng. Hàm start() của chỉ báo Trình
hướng dẫn chỉ chèn một biểu thức vào hàm start(): int count_bars =
IndicatorCounted(); IndicatorCounted() trả về số thanh trên biểu đồ mà chỉ báo đã tính
toán. Khi EA được khởi động lần đầu tiên, giá trị này sẽ là 0. Chỉ báo sẽ được tính cho
mọi thanh trên biểu đồ. Trên các thanh tiếp theo, chúng ta sẽ kiểm tra hàm
IndicatorCounted() để xem có bao nhiêu thanh đã được tính toán, từ đó chúng ta sẽ
biết chính xác có bao nhiêu thanh mới cần tính toán. Việc tính toán chỉ báo của chúng
tôi sẽ diễn ra bên trong vòng lặp for. Điểm bắt đầu sẽ là thanh chưa được tính toán
đầu tiên và điểm kết thúc sẽ là thanh hiện tại. Chúng ta sẽ so sánh giá trị của
IndicatorCounted() với biến Bars được xác định trước, biến này trả về số lượng thanh
trên biểu đồ hiện tại. Điều này sẽ xác định điểm khởi đầu của chúng tôi. Đây là mã cho
vòng lặp for: int count_bars = IndicatorCounted(); if(counted_bars > 0) count_bars--; int
CalculateBars = Thanh - count_bars; for(int Count = CalculateBars; Count >= 0; Count-
-) { // Tính toán chỉ báo } Câu lệnh if đầu tiên sẽ giảm giá trị của count_bars đi 1 khi
tính toán các thanh mới. Chúng tôi sẽ luôn tính toán ít nhất hai thanh trước đó. Điều
này là do điều kiện có thể không tính được dấu tích cuối cùng của thanh trong một số
trường hợp. Tiếp theo, chúng tôi xác định số lượng thanh cần tính toán bằng cách trừ
count_bars khỏi biến Bars được xác định trước. Điều này được lưu trữ trong biến
CalculateBars. 150 www.ZTCprep.com Chỉ báo và Tập lệnh Tùy chỉnh Trong vòng lặp
for của chúng tôi, biến tăng Count được đặt thành giá trị của CalculateBars, điều kiện
để kết thúc là khi Count nhỏ hơn 0 và biến Count được giảm dần sau mỗi lần lặp. Điều
này sẽ tính toán từng thanh trên biểu đồ từ trái sang phải. Đây là mã để tính Dải
Bollinger của chúng tôi. Chúng ta sẽ khai báo biến ngoài BandsPeriod ở đầu tệp. Vòng
lặp for là vòng lặp chúng ta đã tạo ở trên: // Các tham số bên ngoài extern int
BandsPeriod = 20; // hàm start() for(int Count = CalculateBars; Count >= 0; Count--) {
EMA[Count] = iMA(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); UpperBand[Count] = EMA[Count]
+ StdDev; LowerBand[Count] = EMA[Count] - StdDev; } Đầu tiên, chúng ta gọi chỉ báo
Trung bình trượt tích hợp bằng hàm iMA() và gán giá trị trả về cho EMA[Count]. Lưu ý
rằng chỉ mục mảng và tham số Shift cho chỉ báo trung bình động đều sử dụng giá trị
Đếm hiện tại. Tiếp theo, chúng tôi gọi chỉ báo Độ lệch chuẩn bằng iStdDev(). Để tính
dải trên, tất cả những gì chúng ta cần làm là cộng độ lệch chuẩn vào đường trung bình
động. Điều này được lưu trữ trong mảng đệm UpperBand[]. Để tính LowerBand[],
chúng tôi trừ đi độ lệch chuẩn khỏi mức trung bình động. Hãy mở rộng chỉ báo của
chúng tôi thêm một chút bằng cách cung cấp cho nó đầy đủ các cài đặt. Chúng tôi sẽ
thêm cài đặt để điều chỉnh dịch chuyển kỳ hạn, phương pháp trung bình động và các
tham số giá được áp dụng cũng như điều chỉnh độ lệch chuẩn: // Tham số bên ngoài
extern int BandsPeriod = 20; extern int BandsShift = 0; extern int BandsMethod = 1;
extern int BandsPrice = 0; extern int Độ lệch = 1; 151 www.ZTCprep.com LẬP TRÌNH
TƯ VẤN CHUYÊN NGHIỆP // start() function for(int Count = CalculateBars; Count >= 0;
Count--) { EMA[Count] =
iMA(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice ,Đếm); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice,Count);
UpperBand[Count] = EMA[Count] + (StdDev * Độ lệch); LowerBand[Count] =
EMA[Count] - (StdDev * Độ lệch); } Chúng tôi đã thêm các biến bên ngoài để điều chỉnh
các tham số còn lại cho hàm iMA() và iStdDev(). Chúng tôi cũng đã thêm một tham số
để điều chỉnh số lượng độ lệch chuẩn. Để tính toán điều này, chúng tôi chỉ cần nhân
StdDev với Độ lệch. Bây giờ chúng ta có chỉ báo Dải bollinger có thể điều chỉnh hoàn
toàn, linh hoạt hơn chỉ báo MetaTrader tiêu chuẩn. Mã đầy đủ được liệt kê trong Phụ
lục E. Bạn có thể làm được nhiều việc hơn với các chỉ báo tùy chỉnh thay vì chỉ tính
toán lại các chỉ báo tích hợp. Tùy thuộc vào trình độ kiến thức toán học của bạn, bạn
có thể mã hóa các chỉ báo không có trong MetaTrader hoặc thậm chí tạo chỉ báo của
riêng bạn. Bạn cũng có thể vẽ và thao tác các đối tượng. Nếu bạn muốn tìm hiểu thêm
về cách tạo chỉ báo tùy chỉnh, hãy xem chủ đề Tham khảo MQL Chỉ báo tùy chỉnh,
Hàm đối tượng và Toán & Lượng giác. Tập lệnh Tập lệnh là một chương trình MQL chỉ
chạy một lần khi nó được đính kèm lần đầu tiên vào biểu đồ. Tập lệnh có thể được sử
dụng để tự động hóa một loạt hành động giao dịch, chẳng hạn như đóng tất cả các
lệnh trên biểu đồ hoặc gửi lệnh chờ xử lý. Một số tập lệnh, chẳng hạn như tập lệnh
Period_converter đi kèm với MetaTrader, có thể vẽ lại biểu đồ dựa trên khoảng thời
gian tùy chỉnh. Tệp mã nguồn tập lệnh phải có chỉ thị thuộc tính show_confirm hoặc
show_inputs. Thuộc tính show_confirm nhắc người dùng xác nhận hoạt động của tập
lệnh, trong khi show_inputs hiển thị hộp thoại thuộc tính tập lệnh. #property
show_confirm // hiển thị hộp thoại xác nhận #property show_inputs // hiển thị hộp
thoại thuộc tính Nếu tập lệnh của bạn có các tham số cần điều chỉnh, hãy sử dụng
thuộc tính show_inputs. Nếu không, hãy sử dụng show_confirm. 152
www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Cũng giống như các chỉ báo và
cố vấn chuyên gia, các tập lệnh sử dụng các hàm init(), deinit() và start(). Hãy nhớ
rằng mỗi hàm sẽ chỉ được chạy một lần – init() và start() khi tập lệnh được khởi động
và deinit() khi tập lệnh bị xóa. Bạn có thể đính kèm một tập lệnh vào biểu đồ tại một
thời điểm. MetaTrader đi kèm với một số tập lệnh mẫu. Tất cả các tập lệnh được lưu
trong thư mục \experts\scripts. 153 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục A Cố vấn chuyên gia đơn giản Đây là chuyên gia cố vấn đơn
giản từ chương 2. #property bản quyền "Andrew Young" // Biến ngoài extern double
LotSize = 0.1; StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern
int Trượt = 5; extern int MagicNumber = 123; extern int FastMAPeriod = 10; extern int
SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int BánTicket; điểm sử dụng gấp
đôi; int UseTrượt trượt; // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol());
UseSlippage = GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường
trung bình động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi
SlowMA = iMA(NULL,0,SlowMAPeriod,0,0,0,0); 154 www.ZTCprep.com Phụ lục A //
Lệnh mua if(FastMA > SlowMA && BuyTicket == 0) {
OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0 &&
SellTicket > 0) { double CloseLots = OrderLots(); gấp đôi Giá đóng cửa = Hỏi; bool Đã
đóng = OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } double
OpenPrice = Hỏi; // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss
= OpenPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit =
OpenPrice + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Order",MagicNumber,0,Green); BánTicket = 0; } //
Lệnh bán if(FastMA < SlowMA && SellTicket == 0) {
OrderSelect(BuyTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0 && BuyTicket >
0) { CloseLots = OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } OpenPrice = Giá thầu;
if(StopLoss > 0) double SellStopLoss = OpenPrice + (StopLoss * UsePoint);
if(TakeProfit > 0) double SellTakeProfit = OpenPrice - (TakeProfit * UsePoint);
SellTicket = OrderSend(Symbol(),OP_SELL,LotSize,OpenPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Sell Order",MagicNumber,0,Red); MuaVé = 0; } 155
www.ZTCprep.com CHUYÊN GIA LẬP TRÌNH TƯ VẤN return(0); } // Hàm điểm Pip
nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits = MarketInfo(Tiền tệ,MODE_DIGITS);
if(CalcDigits == 2 || CalcDigits == 3) gấp đôi CalcPoint = 0,01; khác nếu(CalcDigits == 4
|| CalcDigits == 5) CalcPoint = 0,0001; return(CalcPoint); } // Lấy hàm trượt giá int
GetSlippage(string Money, int SlippagePips) { int CalcDigits =
MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 4) double
CalcSlippage = SlippagePips; else if(CalcDigits == 3 || CalcDigits == 5) CalcSlippage =
SlippagePips * 10; return(CalcSlippage); } Chuyên gia cố vấn đơn giản với các lệnh
đang chờ xử lý Đây là chuyên gia cố vấn đơn giản sử dụng các lệnh dừng đang chờ xử
lý: #property Copyright "Andrew Young" // Biến bên ngoài extern double LotSize = 0.1;
StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern int
PendingPips = 10; extern int Trượt = 5; extern int MagicNumber = 123; extern int
FastMAPeriod = 10; extern int SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int
BánTicket; điểm sử dụng gấp đôi; int UseTrượt trượt; 156 www.ZTCprep.com Phụ lục
A // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol()); UseSlippage =
GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường trung bình
động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,0); // Lệnh mua if(FastMA > SlowMA && BuyTicket ==
0) { OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0
&& SellTicket > 0 && OrderType() == OP_SELL) { double CloseLots = OrderLots(); gấp
đôi Giá đóng cửa = Hỏi; bool Đã đóng =
OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } // Xóa đơn hàng khác
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_SELLSTOP) { bool
Đã xóa = OrderDelete(SellTicket,Red); } double PendingPrice = Cao[0] + (PendingPips *
UsePoint); // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss =
PendingPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit = Đang
chờ xử lý + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Stop Order",MagicNumber,0,Green); BánTicket = 0; }
157 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA // Bán lệnh if(FastMA <
SlowMA && SellTicket == 0) { OrderSelect(BuyTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0 && BuyTicket > 0 && OrderType() == OP_BUY) { CloseLots =
OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } else
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_BUYSTOP) { Đã xóa
= OrderDelete(SellTicket,Red); } Giá đang chờ xử lý = Thấp[0] - (PendingPips *
UsePoint); if(StopLoss > 0) double SellStopLoss = Giá đang chờ xử lý + (StopLoss *
UsePoint); if(TakeProfit > 0) double SellTakeProfit = Đang chờ xử lý - (TakeProfit *
UsePoint); SellTicket =
OrderSend(Symbol(),OP_SELLSTOP,LotSize,PendingPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Lệnh dừng bán",MagicNumber,0,Red); MuaVé = 0; } trả
về(0); } // Hàm điểm Pip nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits =
MarketInfo(Tiền tệ,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 3) gấp đôi
CalcPoint = 0,01; khác nếu(CalcDigits == 4 || CalcDigits == 5) CalcPoint = 0,0001;
return(CalcPoint); } // Lấy hàm trượt giá int GetSlippage(string Money, int
SlippagePips) { int CalcDigits = MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2
|| CalcDigits == 4) double CalcSlippage = SlippagePips; else if(CalcDigits == 3 ||
CalcDigits == 5) CalcSlippage = SlippagePips * 10; return(CalcSlippage); } 158
www.ZTCprep.com Phụ lục A 159 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục B Cố vấn chuyên gia nâng cao Đây là chuyên gia cố vấn với
các tính năng nâng cao từ chương 3. #property Copyright "Andrew Young" #include
hãy tập trung nỗ lực vào vấn đề được chỉ ra bởi thông báo lỗi. Dưới đây là một số
thông báo lỗi phổ biến nhất do lỗi lập trình gây ra: • Lỗi 129: Giá không hợp lệ – Giá mở
cửa không hợp lệ. Đối với các lệnh thị trường, hãy đảm bảo giá Mua hoặc Bán hiện tại
đang được thông qua, tùy theo loại lệnh. Đối với các lệnh đang chờ xử lý, hãy đảm bảo
giá cao hơn hoặc thấp hơn giá hiện tại, theo yêu cầu của loại lệnh. Đồng thời kiểm tra
xem giá lệnh chờ xử lý có quá gần với giá hiện tại hay không (tức là nằm trong mức
dừng). • Lỗi 130: Điểm dừng không hợp lệ – Giá dừng lỗ hoặc chốt lãi không chính xác.
Kiểm tra xem giá dừng lỗ và chốt lời được đặt ở trên hay dưới giá hiện tại, tùy thuộc
vào loại lệnh là mua hay bán. Đồng thời kiểm tra xem giá dừng lỗ hoặc chốt lãi không
quá gần với giá hiện tại (tức là nằm trong mức dừng). • Lỗi 131: Khối lượng giao dịch
không hợp lệ – Kích thước lô không chính xác. Đảm bảo rằng kích thước lô không vượt
quá mức tối thiểu hoặc tối đa của nhà môi giới và kích thước lô được chuẩn hóa thành
giá trị bước chính xác (0,1 hoặc 0,01 trên hầu hết các nhà môi giới). Bạn có thể tìm
thấy mô tả của tất cả các thông báo lỗi trong Tham chiếu MQL trong Hằng số tiêu
chuẩn – Mã lỗi. Nếu bạn cần hỗ trợ thêm về lỗi mà bạn đang gặp phải, hãy kiểm tra
các diễn đàn tại MQL4.com. Khắc phục sự cố các lỗi giao dịch không liên tục Trong
khi hầu hết các lỗi nghiêm trọng có thể được tìm thấy đơn giản bằng cách kiểm tra lại,
những lỗi khác sẽ chỉ xảy ra trong quá trình giao dịch theo thời gian thực. Lỗi logic có
thể dẫn đến giao dịch không được đặt chính xác và bạn có thể phải mất một chút nỗ
lực để xác định những lỗi này. Nếu có giao dịch được đặt không chính xác trong quá
trình giao dịch demo hoặc giao dịch trực tiếp, chúng tôi cần nhiều thông tin cần thiết
để khắc phục sự cố. Chúng tôi sẽ thêm một tính năng tùy chọn để ghi nhật ký thông
tin trạng thái và giao dịch theo thời gian thực để chúng tôi có bản ghi về nó khi khắc
phục sự cố giao dịch. Chúng tôi sẽ sử dụng câu lệnh Print() như trước đây nhưng
chúng tôi sẽ ghi lại các giá trị chỉ báo, giá cả – bất kỳ thông tin nào hữu ích trong việc
gỡ lỗi. Chúng tôi cũng sẽ thêm một biến bên ngoài để bật và tắt đăng nhập. // Biến
ngoài extern bool Debug = true; 143 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN
NGHIỆP // Đặt ở gần cuối hàm start() if(Debug == true)
Print(StringConcatenate("Bid:",Bid," Ask:",Ask," MA:" ,MA, " MuaTicket:",BuyTicket,"
SellTicket:",SellTicket)); Đoạn mã trên sẽ ghi lại thông tin về giá và chỉ báo cũng như
nội dung của các biến BuyTicket và SellTicket. Nếu có bất kỳ câu hỏi nào về cách mở
giao dịch hoặc tại sao giao dịch không được mở, nhật ký tại thời điểm cụ thể đó sẽ
hiển thị trạng thái của tất cả các điều kiện giao dịch có liên quan. Bạn có thể bật và tắt
đăng nhập bằng biến ngoài Debug. Câu lệnh debug Print() phải được đặt ở gần cuối
hàm start(), sau tất cả các hàm giao dịch. Nếu bạn đang sử dụng bộ hẹn giờ và/hoặc
tính năng thực thi khi mở thanh, hãy đặt câu lệnh debug Print() bên trong khối bộ hẹn
giờ để nó chỉ chạy khi cần thiết. Nếu không, dòng gỡ lỗi sẽ in ra nhật ký sau mỗi tích
tắc, điều này có thể tạo ra một tệp nhật ký lớn. Sửa lỗi biên dịch Khi bạn biên dịch
Expert Advisor, trình biên dịch sẽ kiểm tra cú pháp đúng và đảm bảo rằng tất cả các
hàm và biến tùy chỉnh đã được khai báo đúng. Nếu bạn bỏ sót nội dung nào đó, trình
biên dịch sẽ dừng và mọi lỗi biên dịch sẽ xuất hiện trong tab Lỗi trong cửa sổ Hộp
công cụ. Khi gặp phải một danh sách dài các lỗi biên dịch, hãy luôn bắt đầu từ lỗi đầu
tiên. Bấm đúp vào lỗi trong danh sách và trình soạn thảo sẽ chuyển trực tiếp đến dòng
có lỗi. Sửa lỗi và biên dịch lại. Đôi khi một lỗi cú pháp đơn giản sẽ dẫn đến một số lỗi
không liên quan, mặc dù chỉ có lỗi đầu tiên là hợp lệ. Đây là danh sách các lỗi biên
dịch thường gặp và cách khắc phục: • Biến không được xác định – Bạn quên khai báo
một biến có kiểu dữ liệu. Nếu đó là biến toàn cục hoặc biến ngoài, hãy khai báo nó ở
đầu tệp. Nếu đó là biến cục bộ, hãy tìm lần xuất hiện đầu tiên và đặt phần khai báo
kiểu dữ liệu trước biến đó. Nếu không, hãy kiểm tra chính tả hoặc kiểu chữ
(hoa/thường) của tên biến. • Biến đã được xác định – Bạn đã khai báo cùng một biến
hai lần. Loại bỏ phần khai báo kiểu dữ liệu khỏi tất cả các khai báo biến trùng lặp. •
Hàm không được xác định – Nếu hàm được đề cập nằm trong tệp bao gồm hoặc thư
viện, hãy đảm bảo rằng lệnh #include hoặc #import được đặt ở đầu tệp và đúng. Nếu
không, hãy kiểm tra chính tả hoặc kiểu chữ của tên hàm và đảm bảo rằng nó tồn tại
trong tệp hiện tại hoặc trong tệp bao gồm hoặc thư viện có liên quan. 144
www.ZTCprep.com Mẹo và thủ thuật • Phép gán bất hợp pháp được sử dụng – Điều
này thường liên quan đến dấu bằng (=). Hãy nhớ rằng một dấu bằng duy nhất dành
cho phép gán biến và hai dấu bằng (==) là toán tử so sánh. Sửa toán tử gán thành toán
tử so sánh thích hợp. • Phép gán dự kiến – Điều này thường liên quan đến toán tử so
sánh "bằng" (==). Bạn đã sử dụng hai dấu bằng thay vì một trong phép gán biến. Sửa
toán tử thành một dấu bằng duy nhất. • Dấu ngoặc đơn bên phải không cân bằng –
Điều này thường xảy ra trong câu lệnh if khi sử dụng dấu ngoặc đơn lồng nhau. Đi đến
dòng được chỉ ra bởi lỗi đầu tiên và chèn dấu ngoặc đơn bên trái vào vị trí thích hợp. •
Dấu ngoặc trái không cân bằng – Đây là một dấu ngoặc đơn khó. Lỗi thường xảy ra ở
cuối dòng chương trình. Về cơ bản bạn đã quên dấu ngoặc đơn ở đâu đó. Kiểm tra kỹ
mã bạn đã chỉnh sửa gần đây và tìm dấu ngoặc đơn bên phải bị thiếu. Bạn có thể phải
nhận xét các dòng mã để xác định vấn đề. • Đếm tham số sai – Bạn có quá ít hoặc
quá nhiều đối số trong một hàm. Kiểm tra kỹ cú pháp hàm trong Tham chiếu MQL và
sửa các đối số. • Cần có dấu chấm phẩy – Có thể bạn đã quên đặt dấu chấm phẩy ở
cuối dòng. Đặt dấu chấm phẩy ở cuối dòng trước. Lưu ý rằng thiếu dấu chấm phẩy
cũng có thể gây ra bất kỳ lỗi nào ở trên, vì vậy hãy nhớ đặt các dấu chấm phẩy đó! 145
www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA Chương 9 Các chỉ báo và tập
lệnh tùy chỉnh Không có cuốn sách nào về MQL sẽ hoàn chỉnh nếu không đề cập đến
các chỉ báo và tập lệnh tùy chỉnh. Các chỉ báo tích hợp trong MetaTrader khá hạn chế,
nhưng may mắn thay MQL cho phép các lập trình viên tạo các chỉ báo của riêng họ.
Nếu bạn đang tìm kiếm một chỉ báo phổ biến không có trong MT4 thì rất có thể ai đó
đã tạo một chỉ báo đó. Chương này sẽ là tổng quan cơ bản về việc tạo chỉ báo tùy
chỉnh. Hầu hết các chỉ báo đều sử dụng các công thức toán học phức tạp và đó là lĩnh
vực của các lập trình viên giàu kinh nghiệm hơn. Tuy nhiên, một chỉ báo không cần
phải phức tạp. Chúng ta sẽ tạo một chỉ báo tùy chỉnh trong chương này chỉ sử dụng
một vài dòng mã. Bộ đệm Bộ đệm là mảng lưu trữ các giá trị chỉ báo và phép tính. Một
chỉ báo tùy chỉnh có thể có tối đa 8 bộ đệm. Bộ đệm sử dụng các chỉ mục, giống như
mảng và có phạm vi từ 0 đến 7. Khi bạn gọi một chỉ báo tùy chỉnh trong chuyên gia tư
vấn bằng cách sử dụng hàm iCustom(), tham số tiếp theo trong hàm là bộ đệm chỉ
báo. Để tìm vùng đệm thích hợp cho dòng chỉ báo, bạn thường kiểm tra mã nguồn, nếu
có. Nếu mã nguồn được định dạng rõ ràng với các tên biến mô tả, bạn sẽ có thể xác
định bộ đệm thích hợp khá dễ dàng. Chúng ta sẽ đề cập đến cách đặt tên thích hợp
cho bộ đệm chỉ báo trong chương này. Tạo chỉ báo tùy chỉnh Hãy xây dựng một chỉ
báo tùy chỉnh bằng cách sử dụng hai chỉ báo MetaTrader tích hợp để tính toán các
đường của chúng tôi. Chúng ta sẽ xây dựng một chỉ báo Dải Bollinger đã được sửa
đổi. Dải Bollinger bao gồm 3 đường – đường trung tâm là đường trung bình động đơn
giản, cùng với đường trên và đường dưới có giá trị được xác định bằng độ lệch chuẩn.
Chúng ta có thể tạo chỉ báo Dải bollinger của riêng mình bằng cách sử dụng các chỉ
báo Trung bình trượt và Độ lệch chuẩn. Chúng tôi muốn tạo một chỉ báo sử dụng
đường trung bình động hàm mũ để tính toán các đường, trái ngược với đường trung
bình động đơn giản. 146 www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Chúng
tôi bắt đầu bằng cách sử dụng trình hướng dẫn để tạo tệp chỉ báo của mình. Chọn Mới
từ menu Tệp hoặc thanh công cụ để mở trình hướng dẫn và tạo chỉ báo tùy chỉnh. Điền
tên chỉ báo và thêm thông số nếu muốn. Trên trang cuối cùng, chúng tôi đã thêm ba
dòng chỉ báo cùng màu. Đây là kết quả của thuật sĩ. Hiện tại chúng ta đã bỏ qua hàm
start(): //+--------------------------------- --------------------------------+ //| EMA Bollinger.mq4 | //|
Andrew trẻ | //| http://www.easyexpertforex.com | //+-----------------------------------------------------
-- -------------------+ #bản quyền tài sản "Andrew Young" #link tài sản
"http://www.easyexpertforex.com" #property Indicator_chart_window #property
Indicator_buffers 3 #property Indicator_color1 DeepSkyBlue #property
Indicator_color2 DeepSkyBlue #property Indicator_color3 DeepSkyBlue //---- đệm gấp
đôi ExtMapBuffer1[]; nhân đôi ExtMapBuffer2[]; nhân đôi ExtMapBuffer3[]; //+--------------
----------------------------------------- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+---
---------------------------------------------------- -------------------+ int init() { //---- chỉ báo
SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,ExtMapBuffer1);
SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,ExtMapBuffer2);
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,ExtMapBuffer3); //---- return(0); } Hãy
chuyển sự chú ý của chúng ta tới các phần tử được in đậm. Khai báo #property đặt
tham số cho bộ đệm chỉ báo của chúng tôi. Thuộc tính Indicator_chart_window vẽ chỉ
báo của chúng tôi trong cửa sổ biểu đồ chính. Nếu chúng ta đang tạo một bộ dao
động và muốn vẽ chỉ báo trong một cửa sổ riêng biệt, thì thay vào đó, chúng ta sẽ sử
dụng thuộc tính Indicator_separate_window. Thuộc tính Indicator_buffers đặt số lượng
bộ đệm cho chỉ báo của chúng tôi. Trong trường hợp này chúng tôi đang sử dụng ba
bộ đệm. Thuộc tính Indicator_color đặt màu của cả ba dòng thành DeepSkyBlue. 147
www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN NGHIỆP Tiếp theo là các khai báo
cho mảng bộ đệm của chúng ta. Chúng tôi có ba bộ đệm có tên ExtMapBuffer(1-3).
Chúng tôi sẽ sớm thay đổi các mã định danh mảng này thành một cái gì đó mang tính
mô tả hơn. Hàm init() là nơi chúng ta đặt các thuộc tính cho bộ đệm chỉ báo của mình.
SetIndexBuffer() liên kết một mảng bộ đệm với một chỉ mục bộ đệm. Chỉ mục bộ đệm
là những gì chúng tôi đề cập đến khi đặt thuộc tính cho một dòng chỉ báo cũng như
khi chúng tôi gọi một dòng chỉ báo từ EA bằng cách sử dụng hàm iCustom(). Tham số
đầu tiên là một số nguyên từ 0 đến 7 và tham số thứ hai là tên của mảng đệm. Các
thuộc tính của bản vẽ Hàm SetIndexStyle() thiết lập loại đường để vẽ, cùng với các
thuộc tính của đường đó. Mỗi dòng chỉ báo sẽ có hàm SetIndexStyle() tương ứng. Đây
là cú pháp: void SetIndexStyle(int BufferIndex, int LineType, int LineStyle = EMPTY, int
LineWidth = EMPTY, color LineColor = CLR_NONE) • BufferIndex – Chỉ mục của bộ
đệm, từ 0 đến 7. • LineType – Đặt loại bộ đệm đường để vẽ. DRAW_LINE vẽ một
đường duy nhất, DRAW_HISTOGRAM vẽ biểu đồ dọc (xem ví dụ về OsMA hoặc Bộ tạo
dao động tuyệt vời), DRAW_ARROW vẽ một biểu tượng và DRAW_NONE không vẽ
đường nào. • LineStyle – Một tham số tùy chọn cho biết kiểu vẽ. Được sử dụng chủ
yếu cho các dòng loại DRAW_LINE. Theo mặc định, một đường liền nét được vẽ
(STYLE_SOLID). Bạn cũng có thể vẽ các đường đứt nét (STYLE_DASH) và chấm
(STYLE_DOT). • LineWidth – Một tham số tùy chọn cho biết chiều rộng của đường tính
bằng pixel. Giá trị mặc định là 1. • LineColor – Một tham số tùy chọn cho biết màu của
đường. Nếu bạn sử dụng trình hướng dẫn, màu sẽ được đặt bằng cách sử dụng khai
báo #property, nhưng bạn cũng có thể đặt màu ở đây. Nếu bạn đang sử dụng
DRAW_ARROW làm LineType, hàm SetArrow() cho phép bạn đặt ký hiệu phông chữ
Wingdings để vẽ trên biểu đồ. Tham số đầu tiên là chỉ mục bộ đệm và tham số thứ hai
là hằng số nguyên biểu thị ký hiệu cần vẽ. Các ký hiệu có thể được tìm thấy trong
Tham chiếu MQL trong Hằng số tiêu chuẩn – Mã mũi tên. Bạn có thể muốn thêm mô
tả cho các dòng chỉ báo sẽ được hiển thị trong chú giải công cụ hoặc trong cửa sổ dữ
liệu. Để thực hiện việc này, hãy sử dụng hàm SetIndexLabel(). Tham số đầu tiên là chỉ
mục bộ đệm và tham số thứ hai là mô tả văn bản. Chúng tôi sẽ sớm thêm những thứ
này vào chỉ báo của chúng tôi. 148 www.ZTCprep.com Các chỉ báo và tập lệnh tùy
chỉnh Nếu chỉ báo của bạn được vẽ trong một cửa sổ riêng biệt (chẳng hạn như bộ
dao động) và bạn muốn thêm các mức để biểu thị mức quá mua hoặc quá bán (chẳng
hạn như trong các chỉ báo Stochastic hoặc RSI) hoặc mức 0 (chẳng hạn như trong chỉ
báo CCI), bạn có thể sử dụng các hàm SetLevelStyle() và SetLevelValue(). Xem Tham
chiếu MQL trong Chỉ báo tùy chỉnh để biết thêm thông tin. Bạn cũng có thể muốn chỉ
định một tên chỉ báo ngắn gọn sẽ được hiển thị ở góc trên cùng bên trái của cửa sổ
chỉ báo. Sử dụng hàm IndicatorShortName() để đặt giá trị này. Tham số duy nhất là
một chuỗi văn bản sẽ xuất hiện ở góc trên bên trái của cửa sổ chỉ báo, cũng như trong
cửa sổ dữ liệu. Sử dụng tên bộ đệm mô tả Đây là mã chỉ báo được cập nhật của chúng
tôi. Lưu ý rằng chúng tôi đã đổi tên các mảng đệm để mang tính mô tả rõ hơn về chức
năng thực tế của chúng. Chúng tôi đã thay đổi tham số thứ hai của hàm
SetIndexBuffer() để phản ánh tên bộ đệm mới. Chúng tôi cũng đã thêm
SetIndexLabel() cho mỗi dòng để hiển thị tên mô tả trong Cửa sổ dữ liệu. //---- đệm đôi
EMA[]; gấp đôi UpperBand[]; gấp đôi LowerBand[]; //+-----------------------------------------------------
-- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+------------------------------------------
------------- -------------------+ int init() { //---- chỉ báo SetIndexStyle(0,DRAW_LINE);
SetIndexBuffer(0,EMA); SetIndexLabel(0,"EMA"); SetIndexStyle(1,DRAW_LINE);
SetIndexBuffer(1,UpperBand); SetIndexLabel(1,"UpperBand");
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,LowerBand);
SetIndexLabel(2,"LowerBand"); //---- return(0); } Chúng tôi đã đổi tên các mảng vùng
đệm từ tên mặc định (ExtMapBuffer) thành tên mang tính mô tả hơn. EMA[] sẽ là vùng
đệm của chúng tôi cho đường trung tâm và UpperBand[] và LowerBand[] sẽ lần lượt là
dải trên và dải dưới. 149 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Các
hàm SetIndexBuffer() liên kết các chỉ mục bộ đệm với mảng bộ đệm của chúng ta.
EMA là 0, UpperBand là 1 và LowerBand là 2. Lưu ý rằng các dấu ngoặc nhọn bị loại
khỏi tên định danh mảng cho tham số SetIndexBuffer() thứ hai. Các hàm
SetIndexLabel() đặt tên mô tả cho từng vùng đệm chỉ báo. Trong trường hợp này, tên
dòng giống với tên định danh của chúng tôi. Chúng sẽ xuất hiện trên chú giải công cụ
chuột cũng như trong Cửa sổ dữ liệu. Nếu một lập trình viên khác quyết định sử dụng
chỉ báo này trong chuyên gia cố vấn, thì định dạng ở trên sẽ làm rõ chính xác chỉ mục
bộ đệm chỉ báo nào họ nên sử dụng cho mỗi dòng. Hàm start() của chỉ báo Trình
hướng dẫn chỉ chèn một biểu thức vào hàm start(): int count_bars =
IndicatorCounted(); IndicatorCounted() trả về số thanh trên biểu đồ mà chỉ báo đã tính
toán. Khi EA được khởi động lần đầu tiên, giá trị này sẽ là 0. Chỉ báo sẽ được tính cho
mọi thanh trên biểu đồ. Trên các thanh tiếp theo, chúng ta sẽ kiểm tra hàm
IndicatorCounted() để xem có bao nhiêu thanh đã được tính toán, từ đó chúng ta sẽ
biết chính xác có bao nhiêu thanh mới cần tính toán. Việc tính toán chỉ báo của chúng
tôi sẽ diễn ra bên trong vòng lặp for. Điểm bắt đầu sẽ là thanh chưa được tính toán
đầu tiên và điểm kết thúc sẽ là thanh hiện tại. Chúng ta sẽ so sánh giá trị của
IndicatorCounted() với biến Bars được xác định trước, biến này trả về số lượng thanh
trên biểu đồ hiện tại. Điều này sẽ xác định điểm khởi đầu của chúng tôi. Đây là mã cho
vòng lặp for: int count_bars = IndicatorCounted(); if(counted_bars > 0) count_bars--; int
CalculateBars = Thanh - count_bars; for(int Count = CalculateBars; Count >= 0; Count-
-) { // Tính toán chỉ báo } Câu lệnh if đầu tiên sẽ giảm giá trị của count_bars đi 1 khi
tính toán các thanh mới. Chúng tôi sẽ luôn tính toán ít nhất hai thanh trước đó. Điều
này là do điều kiện có thể không tính được dấu tích cuối cùng của thanh trong một số
trường hợp. Tiếp theo, chúng tôi xác định số lượng thanh cần tính toán bằng cách trừ
count_bars khỏi biến Bars được xác định trước. Điều này được lưu trữ trong biến
CalculateBars. 150 www.ZTCprep.com Chỉ báo và Tập lệnh Tùy chỉnh Trong vòng lặp
for của chúng tôi, biến tăng Count được đặt thành giá trị của CalculateBars, điều kiện
để kết thúc là khi Count nhỏ hơn 0 và biến Count được giảm dần sau mỗi lần lặp. Điều
này sẽ tính toán từng thanh trên biểu đồ từ trái sang phải. Đây là mã để tính Dải
Bollinger của chúng tôi. Chúng ta sẽ khai báo biến ngoài BandsPeriod ở đầu tệp. Vòng
lặp for là vòng lặp chúng ta đã tạo ở trên: // Các tham số bên ngoài extern int
BandsPeriod = 20; // hàm start() for(int Count = CalculateBars; Count >= 0; Count--) {
EMA[Count] = iMA(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); UpperBand[Count] = EMA[Count]
+ StdDev; LowerBand[Count] = EMA[Count] - StdDev; } Đầu tiên, chúng ta gọi chỉ báo
Trung bình trượt tích hợp bằng hàm iMA() và gán giá trị trả về cho EMA[Count]. Lưu ý
rằng chỉ mục mảng và tham số Shift cho chỉ báo trung bình động đều sử dụng giá trị
Đếm hiện tại. Tiếp theo, chúng tôi gọi chỉ báo Độ lệch chuẩn bằng iStdDev(). Để tính
dải trên, tất cả những gì chúng ta cần làm là cộng độ lệch chuẩn vào đường trung bình
động. Điều này được lưu trữ trong mảng đệm UpperBand[]. Để tính LowerBand[],
chúng tôi trừ đi độ lệch chuẩn khỏi mức trung bình động. Hãy mở rộng chỉ báo của
chúng tôi thêm một chút bằng cách cung cấp cho nó đầy đủ các cài đặt. Chúng tôi sẽ
thêm cài đặt để điều chỉnh dịch chuyển kỳ hạn, phương pháp trung bình động và các
tham số giá được áp dụng cũng như điều chỉnh độ lệch chuẩn: // Tham số bên ngoài
extern int BandsPeriod = 20; extern int BandsShift = 0; extern int BandsMethod = 1;
extern int BandsPrice = 0; extern int Độ lệch = 1; 151 www.ZTCprep.com LẬP TRÌNH
TƯ VẤN CHUYÊN NGHIỆP // start() function for(int Count = CalculateBars; Count >= 0;
Count--) { EMA[Count] =
iMA(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice ,Đếm); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice,Count);
UpperBand[Count] = EMA[Count] + (StdDev * Độ lệch); LowerBand[Count] =
EMA[Count] - (StdDev * Độ lệch); } Chúng tôi đã thêm các biến bên ngoài để điều chỉnh
các tham số còn lại cho hàm iMA() và iStdDev(). Chúng tôi cũng đã thêm một tham số
để điều chỉnh số lượng độ lệch chuẩn. Để tính toán điều này, chúng tôi chỉ cần nhân
StdDev với Độ lệch. Bây giờ chúng ta có chỉ báo Dải bollinger có thể điều chỉnh hoàn
toàn, linh hoạt hơn chỉ báo MetaTrader tiêu chuẩn. Mã đầy đủ được liệt kê trong Phụ
lục E. Bạn có thể làm được nhiều việc hơn với các chỉ báo tùy chỉnh thay vì chỉ tính
toán lại các chỉ báo tích hợp. Tùy thuộc vào trình độ kiến thức toán học của bạn, bạn
có thể mã hóa các chỉ báo không có trong MetaTrader hoặc thậm chí tạo chỉ báo của
riêng bạn. Bạn cũng có thể vẽ và thao tác các đối tượng. Nếu bạn muốn tìm hiểu thêm
về cách tạo chỉ báo tùy chỉnh, hãy xem chủ đề Tham khảo MQL Chỉ báo tùy chỉnh,
Hàm đối tượng và Toán & Lượng giác. Tập lệnh Tập lệnh là một chương trình MQL chỉ
chạy một lần khi nó được đính kèm lần đầu tiên vào biểu đồ. Tập lệnh có thể được sử
dụng để tự động hóa một loạt hành động giao dịch, chẳng hạn như đóng tất cả các
lệnh trên biểu đồ hoặc gửi lệnh chờ xử lý. Một số tập lệnh, chẳng hạn như tập lệnh
Period_converter đi kèm với MetaTrader, có thể vẽ lại biểu đồ dựa trên khoảng thời
gian tùy chỉnh. Tệp mã nguồn tập lệnh phải có chỉ thị thuộc tính show_confirm hoặc
show_inputs. Thuộc tính show_confirm nhắc người dùng xác nhận hoạt động của tập
lệnh, trong khi show_inputs hiển thị hộp thoại thuộc tính tập lệnh. #property
show_confirm // hiển thị hộp thoại xác nhận #property show_inputs // hiển thị hộp
thoại thuộc tính Nếu tập lệnh của bạn có các tham số cần được điều chỉnh, hãy sử
dụng thuộc tính show_inputs. Nếu không, hãy sử dụng show_confirm. 152
www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Cũng giống như các chỉ báo và
cố vấn chuyên gia, các tập lệnh sử dụng các hàm init(), deinit() và start(). Hãy nhớ
rằng mỗi hàm sẽ chỉ được chạy một lần – init() và start() khi tập lệnh được khởi động
và deinit() khi tập lệnh bị xóa. Bạn có thể đính kèm một tập lệnh vào biểu đồ tại một
thời điểm. MetaTrader đi kèm với một số tập lệnh mẫu. Tất cả các tập lệnh được lưu
trong thư mục \experts\scripts. 153 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục A Cố vấn chuyên gia đơn giản Đây là chuyên gia cố vấn đơn
giản từ chương 2. #property bản quyền "Andrew Young" // Biến ngoài extern double
LotSize = 0.1; StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern
int Trượt = 5; extern int MagicNumber = 123; extern int FastMAPeriod = 10; extern int
SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int BánTicket; điểm sử dụng gấp
đôi; int UseTrượt trượt; // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol());
UseSlippage = GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường
trung bình động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi
SlowMA = iMA(NULL,0,SlowMAPeriod,0,0,0,0); 154 www.ZTCprep.com Phụ lục A //
Lệnh mua if(FastMA > SlowMA && BuyTicket == 0) {
OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0 &&
SellTicket > 0) { double CloseLots = OrderLots(); gấp đôi Giá đóng cửa = Hỏi; bool Đã
đóng = OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } double
OpenPrice = Hỏi; // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss
= OpenPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit =
OpenPrice + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Order",MagicNumber,0,Green); BánTicket = 0; } //
Lệnh bán if(FastMA < SlowMA && SellTicket == 0) {
OrderSelect(BuyTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0 && BuyTicket >
0) { CloseLots = OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } OpenPrice = Giá thầu;
if(StopLoss > 0) double SellStopLoss = OpenPrice + (StopLoss * UsePoint);
if(TakeProfit > 0) double SellTakeProfit = OpenPrice - (TakeProfit * UsePoint);
SellTicket = OrderSend(Symbol(),OP_SELL,LotSize,OpenPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Sell Order",MagicNumber,0,Red); MuaVé = 0; } 155
www.ZTCprep.com CHUYÊN GIA LẬP TRÌNH TƯ VẤN return(0); } // Hàm điểm Pip
nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits = MarketInfo(Tiền tệ,MODE_DIGITS);
if(CalcDigits == 2 || CalcDigits == 3) gấp đôi CalcPoint = 0,01; khác nếu(CalcDigits == 4
|| CalcDigits == 5) CalcPoint = 0,0001; return(CalcPoint); } // Lấy hàm trượt giá int
GetSlippage(string Money, int SlippagePips) { int CalcDigits =
MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 4) double
CalcSlippage = SlippagePips; else if(CalcDigits == 3 || CalcDigits == 5) CalcSlippage =
SlippagePips * 10; return(CalcSlippage); } Chuyên gia cố vấn đơn giản với các lệnh
đang chờ xử lý Đây là chuyên gia cố vấn đơn giản sử dụng các lệnh dừng đang chờ xử
lý: #property Copyright "Andrew Young" // Biến bên ngoài extern double LotSize = 0.1;
StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern int
PendingPips = 10; extern int Trượt = 5; extern int MagicNumber = 123; extern int
FastMAPeriod = 10; extern int SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int
BánTicket; điểm sử dụng gấp đôi; int UseTrượt trượt; 156 www.ZTCprep.com Phụ lục
A // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol()); UseSlippage =
GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường trung bình
động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,0); // Lệnh mua if(FastMA > SlowMA && BuyTicket ==
0) { OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0
&& SellTicket > 0 && OrderType() == OP_SELL) { double CloseLots = OrderLots(); gấp
đôi Giá đóng cửa = Hỏi; bool Đã đóng =
OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } // Xóa đơn hàng khác
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_SELLSTOP) { bool
Đã xóa = OrderDelete(SellTicket,Red); } double PendingPrice = Cao[0] + (PendingPips *
UsePoint); // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss =
PendingPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit = Đang
chờ xử lý + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Stop Order",MagicNumber,0,Green); BánTicket = 0; }
157 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA // Bán lệnh if(FastMA <
SlowMA && SellTicket == 0) { OrderSelect(BuyTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0 && BuyTicket > 0 && OrderType() == OP_BUY) { CloseLots =
OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } else
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_BUYSTOP) { Đã xóa
= OrderDelete(SellTicket,Red); } Giá đang chờ xử lý = Thấp[0] - (PendingPips *
UsePoint); if(StopLoss > 0) double SellStopLoss = Giá đang chờ xử lý + (StopLoss *
UsePoint); if(TakeProfit > 0) double SellTakeProfit = Đang chờ xử lý - (TakeProfit *
UsePoint); SellTicket =
OrderSend(Symbol(),OP_SELLSTOP,LotSize,PendingPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Lệnh dừng bán",MagicNumber,0,Red); MuaVé = 0; } trả
về(0); } // Hàm điểm Pip nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits =
MarketInfo(Tiền tệ,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 3) gấp đôi
CalcPoint = 0,01; khác nếu(CalcDigits == 4 || CalcDigits == 5) CalcPoint = 0,0001;
return(CalcPoint); } // Lấy hàm trượt giá int GetSlippage(string Money, int
SlippagePips) { int CalcDigits = MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2
|| CalcDigits == 4) double CalcSlippage = SlippagePips; else if(CalcDigits == 3 ||
CalcDigits == 5) CalcSlippage = SlippagePips * 10; return(CalcSlippage); } 158
www.ZTCprep.com Phụ lục A 159 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục B Cố vấn chuyên gia nâng cao Đây là chuyên gia cố vấn với
các tính năng nâng cao từ chương 3. #property Copyright "Andrew Young" #include
hãy tập trung nỗ lực vào vấn đề được chỉ ra bởi thông báo lỗi. Dưới đây là một số
thông báo lỗi phổ biến nhất do lỗi lập trình gây ra: • Lỗi 129: Giá không hợp lệ – Giá mở
cửa không hợp lệ. Đối với các lệnh thị trường, hãy đảm bảo giá Mua hoặc Bán hiện tại
đang được thông qua, tùy theo loại lệnh. Đối với các lệnh đang chờ xử lý, hãy đảm bảo
giá cao hơn hoặc thấp hơn giá hiện tại, theo yêu cầu của loại lệnh. Đồng thời kiểm tra
xem giá lệnh chờ xử lý có quá gần với giá hiện tại hay không (tức là nằm trong mức
dừng). • Lỗi 130: Điểm dừng không hợp lệ – Giá dừng lỗ hoặc chốt lãi không chính xác.
Kiểm tra xem giá dừng lỗ và chốt lời được đặt ở trên hay dưới giá hiện tại, tùy thuộc
vào loại lệnh là mua hay bán. Đồng thời kiểm tra xem giá dừng lỗ hoặc chốt lãi không
quá gần với giá hiện tại (tức là nằm trong mức dừng). • Lỗi 131: Khối lượng giao dịch
không hợp lệ – Kích thước lô không chính xác. Đảm bảo rằng kích thước lô không vượt
quá mức tối thiểu hoặc tối đa của nhà môi giới và kích thước lô được chuẩn hóa thành
giá trị bước chính xác (0,1 hoặc 0,01 trên hầu hết các nhà môi giới). Bạn có thể tìm
thấy mô tả của tất cả các thông báo lỗi trong Tham chiếu MQL trong Hằng số tiêu
chuẩn – Mã lỗi. Nếu bạn cần hỗ trợ thêm về lỗi mà bạn đang gặp phải, hãy kiểm tra
các diễn đàn tại MQL4.com. Khắc phục sự cố các lỗi giao dịch không liên tục Trong
khi hầu hết các lỗi nghiêm trọng có thể được tìm thấy đơn giản bằng cách kiểm tra lại,
những lỗi khác sẽ chỉ xảy ra trong quá trình giao dịch theo thời gian thực. Lỗi logic có
thể dẫn đến giao dịch không được đặt chính xác và bạn có thể phải mất một chút nỗ
lực để xác định những lỗi này. Nếu có giao dịch được đặt không chính xác trong quá
trình giao dịch demo hoặc giao dịch trực tiếp, chúng tôi cần nhiều thông tin cần thiết
để khắc phục sự cố. Chúng tôi sẽ thêm một tính năng tùy chọn để ghi nhật ký thông
tin trạng thái và giao dịch theo thời gian thực để chúng tôi có bản ghi về nó khi khắc
phục sự cố giao dịch. Chúng tôi sẽ sử dụng câu lệnh Print() như trước đây nhưng
chúng tôi sẽ ghi lại các giá trị chỉ báo, giá cả – bất kỳ thông tin nào hữu ích trong việc
gỡ lỗi. Chúng tôi cũng sẽ thêm một biến bên ngoài để bật và tắt đăng nhập. // Biến
ngoài extern bool Debug = true; 143 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN
NGHIỆP // Đặt ở gần cuối hàm start() if(Debug == true)
Print(StringConcatenate("Bid:",Bid," Ask:",Ask," MA:" ,MA, " MuaTicket:",BuyTicket,"
SellTicket:",SellTicket)); Đoạn mã trên sẽ ghi lại thông tin về giá và chỉ báo cũng như
nội dung của các biến BuyTicket và SellTicket. Nếu có bất kỳ câu hỏi nào về cách mở
giao dịch hoặc tại sao giao dịch không được mở, nhật ký tại thời điểm cụ thể đó sẽ
hiển thị trạng thái của tất cả các điều kiện giao dịch có liên quan. Bạn có thể bật và tắt
đăng nhập bằng biến ngoài Debug. Câu lệnh debug Print() phải được đặt ở gần cuối
hàm start(), sau tất cả các hàm giao dịch. Nếu bạn đang sử dụng bộ hẹn giờ và/hoặc
tính năng thực thi khi mở thanh, hãy đặt câu lệnh debug Print() bên trong khối bộ hẹn
giờ để nó chỉ chạy khi cần thiết. Nếu không, dòng gỡ lỗi sẽ in ra nhật ký sau mỗi tích
tắc, điều này có thể tạo ra một tệp nhật ký lớn. Sửa lỗi biên dịch Khi bạn biên dịch
Expert Advisor, trình biên dịch sẽ kiểm tra cú pháp đúng và đảm bảo rằng tất cả các
hàm và biến tùy chỉnh đã được khai báo đúng. Nếu bạn bỏ sót nội dung nào đó, trình
biên dịch sẽ dừng và mọi lỗi biên dịch sẽ xuất hiện trong tab Lỗi trong cửa sổ Hộp
công cụ. Khi gặp phải một danh sách dài các lỗi biên dịch, hãy luôn bắt đầu từ lỗi đầu
tiên. Bấm đúp vào lỗi trong danh sách và trình soạn thảo sẽ chuyển trực tiếp đến dòng
có lỗi. Sửa lỗi và biên dịch lại. Đôi khi một lỗi cú pháp đơn giản sẽ dẫn đến một số lỗi
không liên quan, mặc dù chỉ có lỗi đầu tiên là hợp lệ. Đây là danh sách các lỗi biên
dịch thường gặp và cách khắc phục: • Biến không được xác định – Bạn quên khai báo
một biến có kiểu dữ liệu. Nếu đó là biến toàn cục hoặc biến ngoài, hãy khai báo nó ở
đầu tệp. Nếu đó là biến cục bộ, hãy tìm lần xuất hiện đầu tiên và đặt phần khai báo
kiểu dữ liệu trước biến đó. Nếu không, hãy kiểm tra chính tả hoặc kiểu chữ
(hoa/thường) của tên biến. • Biến đã được xác định – Bạn đã khai báo cùng một biến
hai lần. Loại bỏ phần khai báo kiểu dữ liệu khỏi tất cả các khai báo biến trùng lặp. •
Hàm không được xác định – Nếu hàm được đề cập nằm trong tệp bao gồm hoặc thư
viện, hãy đảm bảo rằng lệnh #include hoặc #import được đặt ở đầu tệp và đúng. Nếu
không, hãy kiểm tra chính tả hoặc kiểu chữ của tên hàm và đảm bảo rằng nó tồn tại
trong tệp hiện tại hoặc trong tệp bao gồm hoặc thư viện có liên quan. 144
www.ZTCprep.com Mẹo và thủ thuật • Phép gán bất hợp pháp được sử dụng – Điều
này thường liên quan đến dấu bằng (=). Hãy nhớ rằng một dấu bằng duy nhất dành
cho phép gán biến và hai dấu bằng (==) là toán tử so sánh. Sửa toán tử gán thành toán
tử so sánh thích hợp. • Phép gán dự kiến – Điều này thường liên quan đến toán tử so
sánh "bằng" (==). Bạn đã sử dụng hai dấu bằng thay vì một trong phép gán biến. Sửa
toán tử thành một dấu bằng duy nhất. • Dấu ngoặc đơn bên phải không cân bằng –
Điều này thường xảy ra trong câu lệnh if khi sử dụng dấu ngoặc đơn lồng nhau. Đi đến
dòng được chỉ ra bởi lỗi đầu tiên và chèn dấu ngoặc đơn bên trái vào vị trí thích hợp. •
Dấu ngoặc trái không cân bằng – Đây là một dấu ngoặc đơn khó. Lỗi thường xảy ra ở
cuối dòng chương trình. Về cơ bản bạn đã quên dấu ngoặc đơn ở đâu đó. Kiểm tra kỹ
mã bạn đã chỉnh sửa gần đây và tìm dấu ngoặc đơn bên phải bị thiếu. Bạn có thể phải
nhận xét các dòng mã để xác định vấn đề. • Đếm tham số sai – Bạn có quá ít hoặc
quá nhiều đối số trong một hàm. Kiểm tra kỹ cú pháp hàm trong Tham chiếu MQL và
sửa các đối số. • Cần có dấu chấm phẩy – Có thể bạn đã quên đặt dấu chấm phẩy ở
cuối dòng. Đặt dấu chấm phẩy ở cuối dòng trước. Lưu ý rằng thiếu dấu chấm phẩy
cũng có thể gây ra bất kỳ lỗi nào ở trên, vì vậy hãy nhớ đặt các dấu chấm phẩy đó! 145
www.ZTCprep.com CHƯƠNG TRÌNH CỐ VẤN CHUYÊN GIA Chương 9 Các chỉ báo và
tập lệnh tùy chỉnh Không có cuốn sách nào về MQL sẽ hoàn chỉnh nếu không đề cập
đến các chỉ báo và tập lệnh tùy chỉnh. Các chỉ báo tích hợp trong MetaTrader khá hạn
chế, nhưng may mắn thay MQL cho phép các lập trình viên tạo các chỉ báo của riêng
họ. Nếu bạn đang tìm kiếm một chỉ báo phổ biến không có trong MT4 thì rất có thể ai
đó đã tạo một chỉ báo đó. Chương này sẽ là tổng quan cơ bản về việc tạo chỉ báo tùy
chỉnh. Hầu hết các chỉ báo đều sử dụng các công thức toán học phức tạp và đó là lĩnh
vực của các lập trình viên giàu kinh nghiệm hơn. Tuy nhiên, một chỉ báo không cần
phải phức tạp. Chúng ta sẽ tạo một chỉ báo tùy chỉnh trong chương này chỉ sử dụng
một vài dòng mã. Bộ đệm Bộ đệm là mảng lưu trữ các giá trị chỉ báo và phép tính. Một
chỉ báo tùy chỉnh có thể có tối đa 8 bộ đệm. Bộ đệm sử dụng các chỉ mục, giống như
mảng và có phạm vi từ 0 đến 7. Khi bạn gọi một chỉ báo tùy chỉnh trong chuyên gia tư
vấn bằng cách sử dụng hàm iCustom(), tham số tiếp theo trong hàm là bộ đệm chỉ
báo. Để tìm vùng đệm thích hợp cho dòng chỉ báo, bạn thường kiểm tra mã nguồn, nếu
có. Nếu mã nguồn được định dạng rõ ràng với các tên biến mô tả, bạn sẽ có thể xác
định bộ đệm thích hợp khá dễ dàng. Chúng ta sẽ đề cập đến cách đặt tên thích hợp
cho bộ đệm chỉ báo trong chương này. Tạo chỉ báo tùy chỉnh Hãy xây dựng một chỉ
báo tùy chỉnh bằng cách sử dụng hai chỉ báo MetaTrader tích hợp để tính toán các
đường của chúng tôi. Chúng ta sẽ xây dựng một chỉ báo Dải Bollinger đã được sửa
đổi. Dải Bollinger bao gồm 3 đường – đường trung tâm là đường trung bình động đơn
giản, cùng với đường trên và đường dưới có giá trị được xác định bằng độ lệch chuẩn.
Chúng ta có thể tạo chỉ báo Dải bollinger của riêng mình bằng cách sử dụng các chỉ
báo Trung bình trượt và Độ lệch chuẩn. Chúng tôi muốn tạo một chỉ báo sử dụng
đường trung bình động hàm mũ để tính toán các đường, trái ngược với đường trung
bình động đơn giản. 146 www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Chúng
tôi bắt đầu bằng cách sử dụng trình hướng dẫn để tạo tệp chỉ báo của mình. Chọn Mới
từ menu Tệp hoặc thanh công cụ để mở trình hướng dẫn và tạo chỉ báo tùy chỉnh. Điền
tên chỉ báo và thêm thông số nếu muốn. Trên trang cuối cùng, chúng tôi đã thêm ba
dòng chỉ báo cùng màu. Đây là kết quả của thuật sĩ. Hiện tại chúng ta đã bỏ qua hàm
start(): //+--------------------------------- --------------------------------+ //| EMA Bollinger.mq4 | //|
Andrew trẻ | //| http://www.easyexpertforex.com | //+-----------------------------------------------------
-- -------------------+ #bản quyền tài sản "Andrew Young" #link tài sản
"http://www.easyexpertforex.com" #property Indicator_chart_window #property
Indicator_buffers 3 #property Indicator_color1 DeepSkyBlue #property
Indicator_color2 DeepSkyBlue #property Indicator_color3 DeepSkyBlue //---- đệm gấp
đôi ExtMapBuffer1[]; nhân đôi ExtMapBuffer2[]; nhân đôi ExtMapBuffer3[]; //+--------------
----------------------------------------- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+---
---------------------------------------------------- -------------------+ int init() { //---- chỉ báo
SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,ExtMapBuffer1);
SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,ExtMapBuffer2);
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,ExtMapBuffer3); //---- return(0); } Hãy
chuyển sự chú ý của chúng ta tới các phần tử được in đậm. Khai báo #property đặt
tham số cho bộ đệm chỉ báo của chúng tôi. Thuộc tính Indicator_chart_window vẽ chỉ
báo của chúng tôi trong cửa sổ biểu đồ chính. Nếu chúng ta đang tạo một bộ dao
động và muốn vẽ chỉ báo trong một cửa sổ riêng biệt, thì thay vào đó, chúng ta sẽ sử
dụng thuộc tính Indicator_separate_window. Thuộc tính Indicator_buffers đặt số lượng
bộ đệm cho chỉ báo của chúng tôi. Trong trường hợp này chúng tôi đang sử dụng ba
bộ đệm. Thuộc tính Indicator_color đặt màu của cả ba dòng thành DeepSkyBlue. 147
www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN NGHIỆP Tiếp theo là các khai báo
cho mảng bộ đệm của chúng ta. Chúng tôi có ba bộ đệm có tên ExtMapBuffer(1-3).
Chúng tôi sẽ sớm thay đổi các mã định danh mảng này thành một cái gì đó mang tính
mô tả hơn. Hàm init() là nơi chúng ta đặt các thuộc tính cho bộ đệm chỉ báo của mình.
SetIndexBuffer() liên kết một mảng bộ đệm với một chỉ mục bộ đệm. Chỉ mục bộ đệm
là những gì chúng tôi đề cập đến khi đặt thuộc tính cho một dòng chỉ báo cũng như
khi chúng tôi gọi một dòng chỉ báo từ EA bằng cách sử dụng hàm iCustom(). Tham số
đầu tiên là một số nguyên từ 0 đến 7 và tham số thứ hai là tên của mảng đệm. Các
thuộc tính của bản vẽ Hàm SetIndexStyle() thiết lập loại đường để vẽ, cùng với các
thuộc tính của đường đó. Mỗi dòng chỉ báo sẽ có hàm SetIndexStyle() tương ứng. Đây
là cú pháp: void SetIndexStyle(int BufferIndex, int LineType, int LineStyle = EMPTY, int
LineWidth = EMPTY, color LineColor = CLR_NONE) • BufferIndex – Chỉ mục của bộ
đệm, từ 0 đến 7. • LineType – Đặt loại bộ đệm đường để vẽ. DRAW_LINE vẽ một
đường duy nhất, DRAW_HISTOGRAM vẽ biểu đồ dọc (xem ví dụ về OsMA hoặc Bộ tạo
dao động tuyệt vời), DRAW_ARROW vẽ một biểu tượng và DRAW_NONE không vẽ
đường nào. • LineStyle – Một tham số tùy chọn cho biết kiểu vẽ. Được sử dụng chủ
yếu cho các dòng loại DRAW_LINE. Theo mặc định, một đường liền nét được vẽ
(STYLE_SOLID). Bạn cũng có thể vẽ các đường đứt nét (STYLE_DASH) và chấm
(STYLE_DOT). • LineWidth – Một tham số tùy chọn cho biết chiều rộng của đường tính
bằng pixel. Giá trị mặc định là 1. • LineColor – Một tham số tùy chọn cho biết màu của
đường. Nếu bạn sử dụng trình hướng dẫn, màu sẽ được đặt bằng cách sử dụng khai
báo #property, nhưng bạn cũng có thể đặt màu ở đây. Nếu bạn đang sử dụng
DRAW_ARROW làm LineType, hàm SetArrow() cho phép bạn đặt ký hiệu phông chữ
Wingdings để vẽ trên biểu đồ. Tham số đầu tiên là chỉ mục bộ đệm và tham số thứ hai
là hằng số nguyên biểu thị ký hiệu cần vẽ. Các ký hiệu có thể được tìm thấy trong
Tham chiếu MQL trong Hằng số tiêu chuẩn – Mã mũi tên. Bạn có thể muốn thêm mô
tả cho các dòng chỉ báo sẽ được hiển thị trong chú giải công cụ hoặc trong cửa sổ dữ
liệu. Để thực hiện việc này, hãy sử dụng hàm SetIndexLabel(). Tham số đầu tiên là chỉ
mục bộ đệm và tham số thứ hai là mô tả văn bản. Chúng tôi sẽ sớm thêm những thứ
này vào chỉ báo của chúng tôi. 148 www.ZTCprep.com Các chỉ báo và tập lệnh tùy
chỉnh Nếu chỉ báo của bạn được vẽ trong một cửa sổ riêng biệt (chẳng hạn như bộ
dao động) và bạn muốn thêm các mức để biểu thị mức quá mua hoặc quá bán (chẳng
hạn như trong các chỉ báo Stochastic hoặc RSI) hoặc mức 0 (chẳng hạn như trong chỉ
báo CCI), bạn có thể sử dụng các hàm SetLevelStyle() và SetLevelValue(). Xem Tham
chiếu MQL trong Chỉ báo tùy chỉnh để biết thêm thông tin. Bạn cũng có thể muốn chỉ
định một tên chỉ báo ngắn gọn sẽ được hiển thị ở góc trên cùng bên trái của cửa sổ
chỉ báo. Sử dụng hàm IndicatorShortName() để đặt giá trị này. Tham số duy nhất là
một chuỗi văn bản sẽ xuất hiện ở góc trên bên trái của cửa sổ chỉ báo, cũng như trong
cửa sổ dữ liệu. Sử dụng tên bộ đệm mô tả Đây là mã chỉ báo được cập nhật của chúng
tôi. Lưu ý rằng chúng tôi đã đổi tên các mảng đệm để mang tính mô tả rõ hơn về chức
năng thực tế của chúng. Chúng tôi đã thay đổi tham số thứ hai của hàm
SetIndexBuffer() để phản ánh tên bộ đệm mới. Chúng tôi cũng đã thêm
SetIndexLabel() cho mỗi dòng để hiển thị tên mô tả trong Cửa sổ dữ liệu. //---- đệm đôi
EMA[]; gấp đôi UpperBand[]; gấp đôi LowerBand[]; //+-----------------------------------------------------
-- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+------------------------------------------
------------- -------------------+ int init() { //---- chỉ báo SetIndexStyle(0,DRAW_LINE);
SetIndexBuffer(0,EMA); SetIndexLabel(0,"EMA"); SetIndexStyle(1,DRAW_LINE);
SetIndexBuffer(1,UpperBand); SetIndexLabel(1,"UpperBand");
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,LowerBand);
SetIndexLabel(2,"LowerBand"); //---- return(0); } Chúng tôi đã đổi tên các mảng vùng
đệm từ tên mặc định (ExtMapBuffer) thành tên mang tính mô tả hơn. EMA[] sẽ là vùng
đệm của chúng tôi cho đường trung tâm và UpperBand[] và LowerBand[] sẽ lần lượt là
dải trên và dải dưới. 149 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Các
hàm SetIndexBuffer() liên kết các chỉ mục bộ đệm với mảng bộ đệm của chúng ta.
EMA là 0, UpperBand là 1 và LowerBand là 2. Lưu ý rằng các dấu ngoặc nhọn bị loại
khỏi tên định danh mảng cho tham số SetIndexBuffer() thứ hai. Các hàm
SetIndexLabel() đặt tên mô tả cho từng vùng đệm chỉ báo. Trong trường hợp này, tên
dòng giống với tên định danh của chúng tôi. Chúng sẽ xuất hiện trên chú giải công cụ
chuột cũng như trong Cửa sổ dữ liệu. Nếu một lập trình viên khác quyết định sử dụng
chỉ báo này trong chuyên gia cố vấn, thì định dạng ở trên sẽ làm rõ chính xác chỉ mục
bộ đệm chỉ báo nào họ nên sử dụng cho mỗi dòng. Hàm start() của chỉ báo Trình
hướng dẫn chỉ chèn một biểu thức vào hàm start(): int count_bars =
IndicatorCounted(); IndicatorCounted() trả về số thanh trên biểu đồ mà chỉ báo đã tính
toán. Khi EA được khởi động lần đầu tiên, giá trị này sẽ là 0. Chỉ báo sẽ được tính cho
mọi thanh trên biểu đồ. Trên các thanh tiếp theo, chúng ta sẽ kiểm tra hàm
IndicatorCounted() để xem có bao nhiêu thanh đã được tính toán, từ đó chúng ta sẽ
biết chính xác có bao nhiêu thanh mới cần tính toán. Việc tính toán chỉ báo của chúng
tôi sẽ diễn ra bên trong vòng lặp for. Điểm bắt đầu sẽ là thanh chưa được tính toán
đầu tiên và điểm kết thúc sẽ là thanh hiện tại. Chúng ta sẽ so sánh giá trị của
IndicatorCounted() với biến Bars được xác định trước, biến này trả về số lượng thanh
trên biểu đồ hiện tại. Điều này sẽ xác định điểm khởi đầu của chúng tôi. Đây là mã cho
vòng lặp for: int count_bars = IndicatorCounted(); if(counted_bars > 0) count_bars--; int
CalculateBars = Thanh - count_bars; for(int Count = CalculateBars; Count >= 0; Count-
-) { // Tính toán chỉ báo } Câu lệnh if đầu tiên sẽ giảm giá trị của count_bars đi 1 khi
tính toán các thanh mới. Chúng tôi sẽ luôn tính toán ít nhất hai thanh trước đó. Điều
này là do điều kiện có thể không tính được dấu tích cuối cùng của thanh trong một số
trường hợp. Tiếp theo, chúng tôi xác định số lượng thanh cần tính toán bằng cách trừ
count_bars khỏi biến Bars được xác định trước. Điều này được lưu trữ trong biến
CalculateBars. 150 www.ZTCprep.com Chỉ báo và Tập lệnh Tùy chỉnh Trong vòng lặp
for của chúng tôi, biến tăng Count được đặt thành giá trị của CalculateBars, điều kiện
để kết thúc là khi Count nhỏ hơn 0 và biến Count được giảm dần sau mỗi lần lặp. Điều
này sẽ tính toán từng thanh trên biểu đồ từ trái sang phải. Đây là mã để tính Dải
Bollinger của chúng tôi. Chúng ta sẽ khai báo biến ngoài BandsPeriod ở đầu tệp. Vòng
lặp for là vòng lặp chúng ta đã tạo ở trên: // Các tham số bên ngoài extern int
BandsPeriod = 20; // hàm start() for(int Count = CalculateBars; Count >= 0; Count--) {
EMA[Count] = iMA(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); UpperBand[Count] = EMA[Count]
+ StdDev; LowerBand[Count] = EMA[Count] - StdDev; } Đầu tiên, chúng ta gọi chỉ báo
Trung bình trượt tích hợp bằng hàm iMA() và gán giá trị trả về cho EMA[Count]. Lưu ý
rằng chỉ mục mảng và tham số Shift cho chỉ báo trung bình động đều sử dụng giá trị
Đếm hiện tại. Tiếp theo, chúng tôi gọi chỉ báo Độ lệch chuẩn bằng iStdDev(). Để tính
dải trên, tất cả những gì chúng ta cần làm là cộng độ lệch chuẩn vào đường trung bình
động. Điều này được lưu trữ trong mảng đệm UpperBand[]. Để tính LowerBand[],
chúng tôi trừ đi độ lệch chuẩn khỏi mức trung bình động. Hãy mở rộng chỉ báo của
chúng tôi thêm một chút bằng cách cung cấp cho nó đầy đủ các cài đặt. Chúng tôi sẽ
thêm cài đặt để điều chỉnh dịch chuyển kỳ hạn, phương pháp trung bình động và các
tham số giá được áp dụng cũng như điều chỉnh độ lệch chuẩn: // Tham số bên ngoài
extern int BandsPeriod = 20; extern int BandsShift = 0; extern int BandsMethod = 1;
extern int BandsPrice = 0; extern int Độ lệch = 1; 151 www.ZTCprep.com LẬP TRÌNH
TƯ VẤN CHUYÊN NGHIỆP // start() function for(int Count = CalculateBars; Count >= 0;
Count--) { EMA[Count] =
iMA(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice ,Đếm); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice,Count);
UpperBand[Count] = EMA[Count] + (StdDev * Độ lệch); LowerBand[Count] =
EMA[Count] - (StdDev * Độ lệch); } Chúng tôi đã thêm các biến bên ngoài để điều chỉnh
các tham số còn lại cho hàm iMA() và iStdDev(). Chúng tôi cũng đã thêm một tham số
để điều chỉnh số lượng độ lệch chuẩn. Để tính toán điều này, chúng tôi chỉ cần nhân
StdDev với Độ lệch. Bây giờ chúng ta có chỉ báo Dải bollinger có thể điều chỉnh hoàn
toàn, linh hoạt hơn chỉ báo MetaTrader tiêu chuẩn. Mã đầy đủ được liệt kê trong Phụ
lục E. Bạn có thể làm được nhiều việc hơn với các chỉ báo tùy chỉnh thay vì chỉ tính
toán lại các chỉ báo tích hợp. Tùy thuộc vào trình độ kiến thức toán học của bạn, bạn
có thể mã hóa các chỉ báo không có trong MetaTrader hoặc thậm chí tạo chỉ báo của
riêng bạn. Bạn cũng có thể vẽ và thao tác các đối tượng. Nếu bạn muốn tìm hiểu thêm
về cách tạo chỉ báo tùy chỉnh, hãy xem chủ đề Tham khảo MQL Chỉ báo tùy chỉnh,
Hàm đối tượng và Toán & Lượng giác. Tập lệnh Tập lệnh là một chương trình MQL chỉ
chạy một lần khi nó được đính kèm lần đầu tiên vào biểu đồ. Tập lệnh có thể được sử
dụng để tự động hóa một loạt hành động giao dịch, chẳng hạn như đóng tất cả các
lệnh trên biểu đồ hoặc gửi lệnh chờ xử lý. Một số tập lệnh, chẳng hạn như tập lệnh
Period_converter đi kèm với MetaTrader, có thể vẽ lại biểu đồ dựa trên khoảng thời
gian tùy chỉnh. Tệp mã nguồn tập lệnh phải có chỉ thị thuộc tính show_confirm hoặc
show_inputs. Thuộc tính show_confirm nhắc người dùng xác nhận hoạt động của tập
lệnh, trong khi show_inputs hiển thị hộp thoại thuộc tính tập lệnh. #property
show_confirm // hiển thị hộp thoại xác nhận #property show_inputs // hiển thị hộp
thoại thuộc tính Nếu tập lệnh của bạn có các tham số cần được điều chỉnh, hãy sử
dụng thuộc tính show_inputs. Nếu không, hãy sử dụng show_confirm. 152
www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Cũng giống như các chỉ báo và
cố vấn chuyên gia, các tập lệnh sử dụng các hàm init(), deinit() và start(). Hãy nhớ
rằng mỗi hàm sẽ chỉ được chạy một lần – init() và start() khi tập lệnh được khởi động
và deinit() khi tập lệnh bị xóa. Bạn có thể đính kèm một tập lệnh vào biểu đồ tại một
thời điểm. MetaTrader đi kèm với một số tập lệnh mẫu. Tất cả các tập lệnh được lưu
Ố
trong thư mục \experts\scripts. 153 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục A Cố vấn chuyên gia đơn giản Đây là chuyên gia cố vấn đơn
giản từ chương 2. #property bản quyền "Andrew Young" // Biến ngoài extern double
LotSize = 0.1; StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern
int Trượt = 5; extern int MagicNumber = 123; extern int FastMAPeriod = 10; extern int
SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int BánTicket; điểm sử dụng gấp
đôi; int UseTrượt trượt; // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol());
UseSlippage = GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường
trung bình động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi
SlowMA = iMA(NULL,0,SlowMAPeriod,0,0,0,0); 154 www.ZTCprep.com Phụ lục A //
Lệnh mua if(FastMA > SlowMA && BuyTicket == 0) {
OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0 &&
SellTicket > 0) { double CloseLots = OrderLots(); gấp đôi Giá đóng cửa = Hỏi; bool Đã
đóng = OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } double
OpenPrice = Hỏi; // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss
= OpenPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit =
OpenPrice + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Order",MagicNumber,0,Green); BánTicket = 0; } //
Lệnh bán if(FastMA < SlowMA && SellTicket == 0) {
OrderSelect(BuyTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0 && BuyTicket >
0) { CloseLots = OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } OpenPrice = Giá thầu;
if(StopLoss > 0) double SellStopLoss = OpenPrice + (StopLoss * UsePoint);
if(TakeProfit > 0) double SellTakeProfit = OpenPrice - (TakeProfit * UsePoint);
SellTicket = OrderSend(Symbol(),OP_SELL,LotSize,OpenPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Sell Order",MagicNumber,0,Red); MuaVé = 0; } 155
www.ZTCprep.com CHUYÊN GIA LẬP TRÌNH TƯ VẤN return(0); } // Hàm điểm Pip
nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits = MarketInfo(Tiền tệ,MODE_DIGITS);
if(CalcDigits == 2 || CalcDigits == 3) gấp đôi CalcPoint = 0,01; khác nếu(CalcDigits == 4
|| CalcDigits == 5) CalcPoint = 0,0001; return(CalcPoint); } // Lấy hàm trượt giá int
GetSlippage(string Money, int SlippagePips) { int CalcDigits =
MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 4) double
CalcSlippage = SlippagePips; else if(CalcDigits == 3 || CalcDigits == 5) CalcSlippage =
SlippagePips * 10; return(CalcSlippage); } Chuyên gia cố vấn đơn giản với các lệnh
đang chờ xử lý Đây là chuyên gia cố vấn đơn giản sử dụng các lệnh dừng đang chờ xử
lý: #property Copyright "Andrew Young" // Biến bên ngoài extern double LotSize = 0.1;
StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern int
PendingPips = 10; extern int Trượt = 5; extern int MagicNumber = 123; extern int
FastMAPeriod = 10; extern int SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int
BánTicket; điểm sử dụng gấp đôi; int UseTrượt trượt; 156 www.ZTCprep.com Phụ lục
A // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol()); UseSlippage =
GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường trung bình
động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,0); // Lệnh mua if(FastMA > SlowMA && BuyTicket ==
0) { OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0
&& SellTicket > 0 && OrderType() == OP_SELL) { double CloseLots = OrderLots(); gấp
đôi Giá đóng cửa = Hỏi; bool Đã đóng =
OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } // Xóa đơn hàng khác
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_SELLSTOP) { bool
Đã xóa = OrderDelete(SellTicket,Red); } double PendingPrice = Cao[0] + (PendingPips *
UsePoint); // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss =
PendingPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit = Đang
chờ xử lý + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Stop Order",MagicNumber,0,Green); BánTicket = 0; }
157 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA // Bán lệnh if(FastMA <
SlowMA && SellTicket == 0) { OrderSelect(BuyTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0 && BuyTicket > 0 && OrderType() == OP_BUY) { CloseLots =
OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } else
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_BUYSTOP) { Đã xóa
= OrderDelete(SellTicket,Red); } Giá đang chờ xử lý = Thấp[0] - (PendingPips *
UsePoint); if(StopLoss > 0) double SellStopLoss = Giá đang chờ xử lý + (StopLoss *
UsePoint); if(TakeProfit > 0) double SellTakeProfit = Đang chờ xử lý - (TakeProfit *
UsePoint); SellTicket =
OrderSend(Symbol(),OP_SELLSTOP,LotSize,PendingPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Lệnh dừng bán",MagicNumber,0,Red); MuaVé = 0; } trả
về(0); } // Hàm điểm Pip nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits =
MarketInfo(Tiền tệ,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 3) gấp đôi
CalcPoint = 0,01; khác nếu(CalcDigits == 4 || CalcDigits == 5) CalcPoint = 0,0001;
return(CalcPoint); } // Lấy hàm trượt giá int GetSlippage(string Money, int
SlippagePips) { int CalcDigits = MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2
|| CalcDigits == 4) double CalcSlippage = SlippagePips; else if(CalcDigits == 3 ||
CalcDigits == 5) CalcSlippage = SlippagePips * 10; return(CalcSlippage); } 158
www.ZTCprep.com Phụ lục A 159 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục B Cố vấn chuyên gia nâng cao Đây là chuyên gia cố vấn với
các tính năng nâng cao từ chương 3. #property Copyright "Andrew Young" #include
Nếu không, dòng gỡ lỗi sẽ in ra nhật ký sau mỗi tích tắc, điều này có thể tạo ra một tệp
nhật ký lớn. Sửa lỗi biên dịch Khi bạn biên dịch Expert Advisor, trình biên dịch sẽ kiểm
tra cú pháp đúng và đảm bảo rằng tất cả các hàm và biến tùy chỉnh đã được khai báo
đúng. Nếu bạn bỏ sót nội dung nào đó, trình biên dịch sẽ dừng và mọi lỗi biên dịch sẽ
xuất hiện trong tab Lỗi trong cửa sổ Hộp công cụ. Khi gặp phải một danh sách dài các
lỗi biên dịch, hãy luôn bắt đầu từ lỗi đầu tiên. Bấm đúp vào lỗi trong danh sách và trình
soạn thảo sẽ chuyển trực tiếp đến dòng có lỗi. Sửa lỗi và biên dịch lại. Đôi khi một lỗi
cú pháp đơn giản sẽ dẫn đến một số lỗi không liên quan, mặc dù chỉ có lỗi đầu tiên là
hợp lệ. Đây là danh sách các lỗi biên dịch thường gặp và cách khắc phục: • Biến không
được xác định – Bạn quên khai báo một biến có kiểu dữ liệu. Nếu đó là biến toàn cục
hoặc biến ngoài, hãy khai báo nó ở đầu tệp. Nếu đó là biến cục bộ, hãy tìm lần xuất
hiện đầu tiên và đặt phần khai báo kiểu dữ liệu trước biến đó. Nếu không, hãy kiểm tra
chính tả hoặc kiểu chữ (hoa/thường) của tên biến. • Biến đã được xác định – Bạn đã
khai báo cùng một biến hai lần. Loại bỏ phần khai báo kiểu dữ liệu khỏi tất cả các khai
báo biến trùng lặp. • Hàm không được xác định – Nếu hàm được đề cập nằm trong
tệp bao gồm hoặc thư viện, hãy đảm bảo rằng lệnh #include hoặc #import được đặt ở
đầu tệp và đúng. Nếu không, hãy kiểm tra chính tả hoặc kiểu chữ của tên hàm và đảm
bảo rằng nó tồn tại trong tệp hiện tại hoặc trong tệp bao gồm hoặc thư viện có liên
quan. 144 www.ZTCprep.com Mẹo và thủ thuật • Phép gán bất hợp pháp được sử
dụng – Điều này thường liên quan đến dấu bằng (=). Hãy nhớ rằng một dấu bằng duy
nhất dành cho phép gán biến và hai dấu bằng (==) là toán tử so sánh. Sửa toán tử gán
thành toán tử so sánh thích hợp. • Phép gán dự kiến – Điều này thường liên quan đến
toán tử so sánh "bằng" (==). Bạn đã sử dụng hai dấu bằng thay vì một trong phép gán
biến. Sửa toán tử thành một dấu bằng duy nhất. • Dấu ngoặc đơn bên phải không cân
bằng – Điều này thường xảy ra trong câu lệnh if khi sử dụng dấu ngoặc đơn lồng nhau.
Đi đến dòng được chỉ ra bởi lỗi đầu tiên và chèn dấu ngoặc đơn bên trái vào vị trí thích
hợp. • Dấu ngoặc trái không cân bằng – Đây là một dấu ngoặc đơn khó. Lỗi thường
xảy ra ở cuối dòng chương trình. Về cơ bản bạn đã quên dấu ngoặc đơn ở đâu đó.
Kiểm tra kỹ mã bạn đã chỉnh sửa gần đây và tìm dấu ngoặc đơn bên phải bị thiếu. Bạn
có thể phải nhận xét các dòng mã để xác định vấn đề. • Đếm tham số sai – Bạn có
quá ít hoặc quá nhiều đối số trong một hàm. Kiểm tra kỹ cú pháp hàm trong Tham
chiếu MQL và sửa các đối số. • Cần có dấu chấm phẩy – Có thể bạn đã quên đặt dấu
chấm phẩy ở cuối dòng. Đặt dấu chấm phẩy ở cuối dòng trước. Lưu ý rằng thiếu dấu
chấm phẩy cũng có thể gây ra bất kỳ lỗi nào ở trên, vì vậy hãy nhớ đặt các dấu chấm
phẩy đó! 145 www.ZTCprep.com CHƯƠNG TRÌNH CỐ VẤN CHUYÊN GIA Chương 9
Các chỉ báo và tập lệnh tùy chỉnh Không có cuốn sách nào về MQL sẽ hoàn chỉnh nếu
không đề cập đến các chỉ báo và tập lệnh tùy chỉnh. Các chỉ báo tích hợp trong
MetaTrader khá hạn chế, nhưng may mắn thay MQL cho phép các lập trình viên tạo
các chỉ báo của riêng họ. Nếu bạn đang tìm kiếm một chỉ báo phổ biến không có trong
MT4 thì rất có thể ai đó đã tạo một chỉ báo đó. Chương này sẽ là tổng quan cơ bản về
việc tạo chỉ báo tùy chỉnh. Hầu hết các chỉ báo đều sử dụng các công thức toán học
phức tạp và đó là lĩnh vực của các lập trình viên giàu kinh nghiệm hơn. Tuy nhiên, một
chỉ báo không cần phải phức tạp. Chúng ta sẽ tạo một chỉ báo tùy chỉnh trong chương
này chỉ sử dụng một vài dòng mã. Bộ đệm Bộ đệm là mảng lưu trữ các giá trị chỉ báo
và phép tính. Một chỉ báo tùy chỉnh có thể có tối đa 8 bộ đệm. Bộ đệm sử dụng các chỉ
mục, giống như mảng và có phạm vi từ 0 đến 7. Khi bạn gọi một chỉ báo tùy chỉnh
trong chuyên gia tư vấn bằng cách sử dụng hàm iCustom(), tham số tiếp theo trong
hàm là bộ đệm chỉ báo. Để tìm vùng đệm thích hợp cho dòng chỉ báo, bạn thường
kiểm tra mã nguồn, nếu có. Nếu mã nguồn được định dạng rõ ràng với các tên biến mô
tả, bạn sẽ có thể xác định bộ đệm thích hợp khá dễ dàng. Chúng ta sẽ đề cập đến
cách đặt tên thích hợp cho bộ đệm chỉ báo trong chương này. Tạo chỉ báo tùy chỉnh
Hãy xây dựng một chỉ báo tùy chỉnh bằng cách sử dụng hai chỉ báo MetaTrader tích
hợp để tính toán các đường của chúng tôi. Chúng ta sẽ xây dựng một chỉ báo Dải
Bollinger đã được sửa đổi. Dải Bollinger bao gồm 3 đường – đường trung tâm là
đường trung bình động đơn giản, cùng với đường trên và đường dưới có giá trị được
xác định bằng độ lệch chuẩn. Chúng ta có thể tạo chỉ báo Dải bollinger của riêng mình
bằng cách sử dụng các chỉ báo Trung bình trượt và Độ lệch chuẩn. Chúng tôi muốn
tạo một chỉ báo sử dụng đường trung bình động hàm mũ để tính toán các đường, trái
ngược với đường trung bình động đơn giản. 146 www.ZTCprep.com Các chỉ báo và
tập lệnh tùy chỉnh Chúng tôi bắt đầu bằng cách sử dụng trình hướng dẫn để tạo tệp
chỉ báo của mình. Chọn Mới từ menu Tệp hoặc thanh công cụ để mở trình hướng dẫn
và tạo chỉ báo tùy chỉnh. Điền tên chỉ báo và thêm thông số nếu muốn. Trên trang cuối
cùng, chúng tôi đã thêm ba dòng chỉ báo cùng màu. Đây là kết quả của thuật sĩ. Hiện
tại chúng ta đã bỏ qua hàm start(): //+--------------------------------- --------------------------------+ //|
EMA Bollinger.mq4 | //| Andrew trẻ | //| http://www.easyexpertforex.com | //+----------------
--------------------------------------- -------------------+ #bản quyền tài sản "Andrew Young" #link tài
sản "http://www.easyexpertforex.com" #property Indicator_chart_window #property
Indicator_buffers 3 #property Indicator_color1 DeepSkyBlue #property
Indicator_color2 DeepSkyBlue #property Indicator_color3 DeepSkyBlue //---- đệm gấp
đôi ExtMapBuffer1[]; nhân đôi ExtMapBuffer2[]; nhân đôi ExtMapBuffer3[]; //+--------------
----------------------------------------- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+---
---------------------------------------------------- -------------------+ int init() { //---- chỉ báo
SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,ExtMapBuffer1);
SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,ExtMapBuffer2);
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,ExtMapBuffer3); //---- return(0); } Hãy
chuyển sự chú ý của chúng ta tới các phần tử được in đậm. Khai báo #property đặt
tham số cho bộ đệm chỉ báo của chúng tôi. Thuộc tính Indicator_chart_window vẽ chỉ
báo của chúng tôi trong cửa sổ biểu đồ chính. Nếu chúng ta đang tạo một bộ dao
động và muốn vẽ chỉ báo trong một cửa sổ riêng biệt, thì thay vào đó, chúng ta sẽ sử
dụng thuộc tính Indicator_separate_window. Thuộc tính Indicator_buffers đặt số lượng
bộ đệm cho chỉ báo của chúng tôi. Trong trường hợp này chúng tôi đang sử dụng ba
bộ đệm. Thuộc tính Indicator_color đặt màu của cả ba dòng thành DeepSkyBlue. 147
www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN NGHIỆP Tiếp theo là các khai báo
cho mảng bộ đệm của chúng ta. Chúng tôi có ba bộ đệm có tên ExtMapBuffer(1-3).
Chúng tôi sẽ sớm thay đổi các mã định danh mảng này thành một cái gì đó mang tính
mô tả hơn. Hàm init() là nơi chúng ta đặt các thuộc tính cho bộ đệm chỉ báo của mình.
SetIndexBuffer() liên kết một mảng bộ đệm với một chỉ mục bộ đệm. Chỉ mục bộ đệm
là những gì chúng tôi đề cập đến khi đặt thuộc tính cho một dòng chỉ báo cũng như
khi chúng tôi gọi một dòng chỉ báo từ EA bằng cách sử dụng hàm iCustom(). Tham số
đầu tiên là một số nguyên từ 0 đến 7 và tham số thứ hai là tên của mảng đệm. Các
thuộc tính của bản vẽ Hàm SetIndexStyle() thiết lập loại đường để vẽ, cùng với các
thuộc tính của đường đó. Mỗi dòng chỉ báo sẽ có hàm SetIndexStyle() tương ứng. Đây
là cú pháp: void SetIndexStyle(int BufferIndex, int LineType, int LineStyle = EMPTY, int
LineWidth = EMPTY, color LineColor = CLR_NONE) • BufferIndex – Chỉ mục của bộ
đệm, từ 0 đến 7. • LineType – Đặt loại bộ đệm đường để vẽ. DRAW_LINE vẽ một
đường duy nhất, DRAW_HISTOGRAM vẽ biểu đồ dọc (xem ví dụ về OsMA hoặc Bộ tạo
dao động tuyệt vời), DRAW_ARROW vẽ một biểu tượng và DRAW_NONE không vẽ
đường nào. • LineStyle – Một tham số tùy chọn cho biết kiểu vẽ. Được sử dụng chủ
yếu cho các dòng loại DRAW_LINE. Theo mặc định, một đường liền nét được vẽ
(STYLE_SOLID). Bạn cũng có thể vẽ các đường đứt nét (STYLE_DASH) và chấm
(STYLE_DOT). • LineWidth – Một tham số tùy chọn cho biết chiều rộng của đường tính
bằng pixel. Giá trị mặc định là 1. • LineColor – Một tham số tùy chọn cho biết màu của
đường. Nếu bạn sử dụng trình hướng dẫn, màu sẽ được đặt bằng cách sử dụng khai
báo #property, nhưng bạn cũng có thể đặt màu ở đây. Nếu bạn đang sử dụng
DRAW_ARROW làm LineType, hàm SetArrow() cho phép bạn đặt ký hiệu phông chữ
Wingdings để vẽ trên biểu đồ. Tham số đầu tiên là chỉ mục bộ đệm và tham số thứ hai
là hằng số nguyên biểu thị ký hiệu cần vẽ. Các ký hiệu có thể được tìm thấy trong
Tham chiếu MQL trong Hằng số tiêu chuẩn – Mã mũi tên. Bạn có thể muốn thêm mô
tả cho các dòng chỉ báo sẽ được hiển thị trong chú giải công cụ hoặc trong cửa sổ dữ
liệu. Để thực hiện việc này, hãy sử dụng hàm SetIndexLabel(). Tham số đầu tiên là chỉ
mục bộ đệm và tham số thứ hai là mô tả văn bản. Chúng tôi sẽ sớm thêm những thứ
này vào chỉ báo của chúng tôi. 148 www.ZTCprep.com Các chỉ báo và tập lệnh tùy
chỉnh Nếu chỉ báo của bạn được vẽ trong một cửa sổ riêng biệt (chẳng hạn như bộ
dao động) và bạn muốn thêm các mức để biểu thị mức quá mua hoặc quá bán (chẳng
hạn như trong các chỉ báo Stochastic hoặc RSI) hoặc mức 0 (chẳng hạn như trong chỉ
báo CCI), bạn có thể sử dụng các hàm SetLevelStyle() và SetLevelValue(). Xem Tham
chiếu MQL trong Chỉ báo tùy chỉnh để biết thêm thông tin. Bạn cũng có thể muốn chỉ
định một tên chỉ báo ngắn gọn sẽ được hiển thị ở góc trên cùng bên trái của cửa sổ
chỉ báo. Sử dụng hàm IndicatorShortName() để đặt giá trị này. Tham số duy nhất là
một chuỗi văn bản sẽ xuất hiện ở góc trên bên trái của cửa sổ chỉ báo, cũng như trong
cửa sổ dữ liệu. Sử dụng tên bộ đệm mô tả Đây là mã chỉ báo được cập nhật của chúng
tôi. Lưu ý rằng chúng tôi đã đổi tên các mảng đệm để mang tính mô tả rõ hơn về chức
năng thực tế của chúng. Chúng tôi đã thay đổi tham số thứ hai của hàm
SetIndexBuffer() để phản ánh tên bộ đệm mới. Chúng tôi cũng đã thêm
SetIndexLabel() cho mỗi dòng để hiển thị tên mô tả trong Cửa sổ dữ liệu. //---- đệm đôi
EMA[]; gấp đôi UpperBand[]; gấp đôi LowerBand[]; //+-----------------------------------------------------
-- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+------------------------------------------
------------- -------------------+ int init() { //---- chỉ báo SetIndexStyle(0,DRAW_LINE);
SetIndexBuffer(0,EMA); SetIndexLabel(0,"EMA"); SetIndexStyle(1,DRAW_LINE);
SetIndexBuffer(1,UpperBand); SetIndexLabel(1,"UpperBand");
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,LowerBand);
SetIndexLabel(2,"LowerBand"); //---- return(0); } Chúng tôi đã đổi tên các mảng vùng
đệm từ tên mặc định (ExtMapBuffer) thành tên mang tính mô tả hơn. EMA[] sẽ là vùng
đệm của chúng tôi cho đường trung tâm và UpperBand[] và LowerBand[] sẽ lần lượt là
dải trên và dải dưới. 149 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Các
hàm SetIndexBuffer() liên kết các chỉ mục bộ đệm với mảng bộ đệm của chúng ta.
EMA là 0, UpperBand là 1 và LowerBand là 2. Lưu ý rằng các dấu ngoặc nhọn bị loại
khỏi tên định danh mảng cho tham số SetIndexBuffer() thứ hai. Các hàm
SetIndexLabel() đặt tên mô tả cho từng vùng đệm chỉ báo. Trong trường hợp này, tên
dòng giống với tên định danh của chúng tôi. Chúng sẽ xuất hiện trên chú giải công cụ
chuột cũng như trong Cửa sổ dữ liệu. Nếu một lập trình viên khác quyết định sử dụng
chỉ báo này trong chuyên gia cố vấn, thì định dạng ở trên sẽ làm rõ chính xác chỉ mục
bộ đệm chỉ báo nào họ nên sử dụng cho mỗi dòng. Hàm start() của chỉ báo Trình
hướng dẫn chỉ chèn một biểu thức vào hàm start(): int count_bars =
IndicatorCounted(); IndicatorCounted() trả về số thanh trên biểu đồ mà chỉ báo đã tính
toán. Khi EA được khởi động lần đầu tiên, giá trị này sẽ là 0. Chỉ báo sẽ được tính cho
mọi thanh trên biểu đồ. Trên các thanh tiếp theo, chúng ta sẽ kiểm tra hàm
IndicatorCounted() để xem có bao nhiêu thanh đã được tính toán, từ đó chúng ta sẽ
biết chính xác có bao nhiêu thanh mới cần tính toán. Việc tính toán chỉ báo của chúng
tôi sẽ diễn ra bên trong vòng lặp for. Điểm bắt đầu sẽ là thanh chưa được tính toán
đầu tiên và điểm kết thúc sẽ là thanh hiện tại. Chúng ta sẽ so sánh giá trị của
IndicatorCounted() với biến Bars được xác định trước, biến này trả về số lượng thanh
trên biểu đồ hiện tại. Điều này sẽ xác định điểm khởi đầu của chúng tôi. Đây là mã cho
vòng lặp for: int count_bars = IndicatorCounted(); if(counted_bars > 0) count_bars--; int
CalculateBars = Thanh - count_bars; for(int Count = CalculateBars; Count >= 0; Count-
-) { // Tính toán chỉ báo } Câu lệnh if đầu tiên sẽ giảm giá trị của count_bars đi 1 khi
tính toán các thanh mới. Chúng tôi sẽ luôn tính toán ít nhất hai thanh trước đó. Điều
này là do điều kiện có thể không tính được dấu tích cuối cùng của thanh trong một số
trường hợp. Tiếp theo, chúng tôi xác định số lượng thanh cần tính toán bằng cách trừ
count_bars khỏi biến Bars được xác định trước. Điều này được lưu trữ trong biến
CalculateBars. 150 www.ZTCprep.com Chỉ báo và Tập lệnh Tùy chỉnh Trong vòng lặp
for của chúng tôi, biến tăng Count được đặt thành giá trị của CalculateBars, điều kiện
để kết thúc là khi Count nhỏ hơn 0 và biến Count được giảm dần sau mỗi lần lặp. Điều
này sẽ tính toán từng thanh trên biểu đồ từ trái sang phải. Đây là mã để tính Dải
Bollinger của chúng tôi. Chúng ta sẽ khai báo biến ngoài BandsPeriod ở đầu tệp. Vòng
lặp for là vòng lặp chúng ta đã tạo ở trên: // Các tham số bên ngoài extern int
BandsPeriod = 20; // hàm start() for(int Count = CalculateBars; Count >= 0; Count--) {
EMA[Count] = iMA(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); UpperBand[Count] = EMA[Count]
+ StdDev; LowerBand[Count] = EMA[Count] - StdDev; } Đầu tiên, chúng ta gọi chỉ báo
Trung bình trượt tích hợp bằng hàm iMA() và gán giá trị trả về cho EMA[Count]. Lưu ý
rằng chỉ mục mảng và tham số Shift cho chỉ báo trung bình động đều sử dụng giá trị
Đếm hiện tại. Tiếp theo, chúng tôi gọi chỉ báo Độ lệch chuẩn bằng iStdDev(). Để tính
dải trên, tất cả những gì chúng ta cần làm là cộng độ lệch chuẩn vào đường trung bình
động. Điều này được lưu trữ trong mảng đệm UpperBand[]. Để tính LowerBand[],
chúng tôi trừ đi độ lệch chuẩn khỏi mức trung bình động. Hãy mở rộng chỉ báo của
chúng tôi thêm một chút bằng cách cung cấp cho nó đầy đủ các cài đặt. Chúng tôi sẽ
thêm cài đặt để điều chỉnh dịch chuyển kỳ hạn, phương pháp trung bình động và các
tham số giá được áp dụng cũng như điều chỉnh độ lệch chuẩn: // Tham số bên ngoài
extern int BandsPeriod = 20; extern int BandsShift = 0; extern int BandsMethod = 1;
extern int BandsPrice = 0; extern int Độ lệch = 1; 151 www.ZTCprep.com LẬP TRÌNH
TƯ VẤN CHUYÊN NGHIỆP // start() function for(int Count = CalculateBars; Count >= 0;
Count--) { EMA[Count] =
iMA(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice ,Đếm); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice,Count);
UpperBand[Count] = EMA[Count] + (StdDev * Độ lệch); LowerBand[Count] =
EMA[Count] - (StdDev * Độ lệch); } Chúng tôi đã thêm các biến bên ngoài để điều chỉnh
các tham số còn lại cho hàm iMA() và iStdDev(). Chúng tôi cũng đã thêm một tham số
để điều chỉnh số lượng độ lệch chuẩn. Để tính toán điều này, chúng tôi chỉ cần nhân
StdDev với Độ lệch. Bây giờ chúng ta có chỉ báo Dải bollinger có thể điều chỉnh hoàn
toàn, linh hoạt hơn chỉ báo MetaTrader tiêu chuẩn. Mã đầy đủ được liệt kê trong Phụ
lục E. Bạn có thể làm được nhiều việc hơn với các chỉ báo tùy chỉnh thay vì chỉ tính
toán lại các chỉ báo tích hợp. Tùy thuộc vào trình độ kiến thức toán học của bạn, bạn
có thể mã hóa các chỉ báo không có trong MetaTrader hoặc thậm chí tạo chỉ báo của
riêng bạn. Bạn cũng có thể vẽ và thao tác các đối tượng. Nếu bạn muốn tìm hiểu thêm
về cách tạo chỉ báo tùy chỉnh, hãy xem chủ đề Tham khảo MQL Chỉ báo tùy chỉnh,
Hàm đối tượng và Toán & Lượng giác. Tập lệnh Tập lệnh là một chương trình MQL chỉ
chạy một lần khi nó được đính kèm lần đầu tiên vào biểu đồ. Tập lệnh có thể được sử
dụng để tự động hóa một loạt hành động giao dịch, chẳng hạn như đóng tất cả các
lệnh trên biểu đồ hoặc gửi lệnh chờ xử lý. Một số tập lệnh, chẳng hạn như tập lệnh
Period_converter đi kèm với MetaTrader, có thể vẽ lại biểu đồ dựa trên khoảng thời
gian tùy chỉnh. Tệp mã nguồn tập lệnh phải có chỉ thị thuộc tính show_confirm hoặc
show_inputs. Thuộc tính show_confirm nhắc người dùng xác nhận hoạt động của tập
lệnh, trong khi show_inputs hiển thị hộp thoại thuộc tính tập lệnh. #property
show_confirm // hiển thị hộp thoại xác nhận #property show_inputs // hiển thị hộp
thoại thuộc tính Nếu tập lệnh của bạn có các tham số cần điều chỉnh, hãy sử dụng
thuộc tính show_inputs. Nếu không, hãy sử dụng show_confirm. 152
www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Cũng giống như các chỉ báo và
cố vấn chuyên gia, các tập lệnh sử dụng các hàm init(), deinit() và start(). Hãy nhớ
rằng mỗi hàm sẽ chỉ được chạy một lần – init() và start() khi tập lệnh được khởi động
và deinit() khi tập lệnh bị xóa. Bạn có thể đính kèm một tập lệnh vào biểu đồ tại một
thời điểm. MetaTrader đi kèm với một số tập lệnh mẫu. Tất cả các tập lệnh được lưu
trong thư mục \experts\scripts. 153 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục A Cố vấn chuyên gia đơn giản Đây là chuyên gia cố vấn đơn
giản từ chương 2. #property bản quyền "Andrew Young" // Biến ngoài extern double
LotSize = 0.1; StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern
int Trượt = 5; extern int MagicNumber = 123; extern int FastMAPeriod = 10; extern int
SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int BánTicket; điểm sử dụng gấp
đôi; int UseTrượt trượt; // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol());
UseSlippage = GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường
trung bình động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi
SlowMA = iMA(NULL,0,SlowMAPeriod,0,0,0,0); 154 www.ZTCprep.com Phụ lục A //
Lệnh mua if(FastMA > SlowMA && BuyTicket == 0) {
OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0 &&
SellTicket > 0) { double CloseLots = OrderLots(); gấp đôi Giá đóng cửa = Hỏi; bool Đã
đóng = OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } double
OpenPrice = Hỏi; // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss
= OpenPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit =
OpenPrice + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Order",MagicNumber,0,Green); BánTicket = 0; } //
Lệnh bán if(FastMA < SlowMA && SellTicket == 0) {
OrderSelect(BuyTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0 && BuyTicket >
0) { CloseLots = OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } OpenPrice = Giá thầu;
if(StopLoss > 0) double SellStopLoss = OpenPrice + (StopLoss * UsePoint);
if(TakeProfit > 0) double SellTakeProfit = OpenPrice - (TakeProfit * UsePoint);
SellTicket = OrderSend(Symbol(),OP_SELL,LotSize,OpenPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Sell Order",MagicNumber,0,Red); MuaVé = 0; } 155
www.ZTCprep.com CHUYÊN GIA LẬP TRÌNH TƯ VẤN return(0); } // Hàm điểm Pip
nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits = MarketInfo(Tiền tệ,MODE_DIGITS);
if(CalcDigits == 2 || CalcDigits == 3) gấp đôi CalcPoint = 0,01; khác nếu(CalcDigits == 4
|| CalcDigits == 5) CalcPoint = 0,0001; return(CalcPoint); } // Lấy hàm trượt giá int
GetSlippage(string Money, int SlippagePips) { int CalcDigits =
MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 4) double
CalcSlippage = SlippagePips; else if(CalcDigits == 3 || CalcDigits == 5) CalcSlippage =
SlippagePips * 10; return(CalcSlippage); } Chuyên gia cố vấn đơn giản với các lệnh
đang chờ xử lý Đây là chuyên gia cố vấn đơn giản sử dụng các lệnh dừng đang chờ xử
lý: #property Copyright "Andrew Young" // Biến bên ngoài extern double LotSize = 0.1;
StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern int
PendingPips = 10; extern int Trượt = 5; extern int MagicNumber = 123; extern int
FastMAPeriod = 10; extern int SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int
BánTicket; điểm sử dụng gấp đôi; int UseTrượt trượt; 156 www.ZTCprep.com Phụ lục
A // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol()); UseSlippage =
GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường trung bình
động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,0); // Lệnh mua if(FastMA > SlowMA && BuyTicket ==
0) { OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0
&& SellTicket > 0 && OrderType() == OP_SELL) { double CloseLots = OrderLots(); gấp
đôi Giá đóng cửa = Hỏi; bool Đã đóng =
OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } // Xóa đơn hàng khác
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_SELLSTOP) { bool
Đã xóa = OrderDelete(SellTicket,Red); } double PendingPrice = Cao[0] + (PendingPips *
UsePoint); // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss =
PendingPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit = Đang
chờ xử lý + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Stop Order",MagicNumber,0,Green); BánTicket = 0; }
157 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA // Bán lệnh if(FastMA <
SlowMA && SellTicket == 0) { OrderSelect(BuyTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0 && BuyTicket > 0 && OrderType() == OP_BUY) { CloseLots =
OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } else
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_BUYSTOP) { Đã xóa
= OrderDelete(SellTicket,Red); } Giá đang chờ xử lý = Thấp[0] - (PendingPips *
UsePoint); if(StopLoss > 0) double SellStopLoss = Giá đang chờ xử lý + (StopLoss *
UsePoint); if(TakeProfit > 0) double SellTakeProfit = Đang chờ xử lý - (TakeProfit *
UsePoint); SellTicket =
OrderSend(Symbol(),OP_SELLSTOP,LotSize,PendingPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Lệnh dừng bán",MagicNumber,0,Red); MuaVé = 0; } trả
về(0); } // Hàm điểm Pip nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits =
MarketInfo(Tiền tệ,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 3) gấp đôi
CalcPoint = 0,01; khác nếu(CalcDigits == 4 || CalcDigits == 5) CalcPoint = 0,0001;
return(CalcPoint); } // Lấy hàm trượt giá int GetSlippage(string Money, int
SlippagePips) { int CalcDigits = MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2
|| CalcDigits == 4) double CalcSlippage = SlippagePips; else if(CalcDigits == 3 ||
CalcDigits == 5) CalcSlippage = SlippagePips * 10; return(CalcSlippage); } 158
www.ZTCprep.com Phụ lục A 159 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục B Cố vấn chuyên gia nâng cao Đây là chuyên gia cố vấn với
các tính năng nâng cao từ chương 3. #property Copyright "Andrew Young" #include
Nếu không, dòng gỡ lỗi sẽ in ra nhật ký sau mỗi tích tắc, điều này có thể tạo ra một tệp
nhật ký lớn. Sửa lỗi biên dịch Khi bạn biên dịch Expert Advisor, trình biên dịch sẽ kiểm
tra cú pháp đúng và đảm bảo rằng tất cả các hàm và biến tùy chỉnh đã được khai báo
đúng. Nếu bạn bỏ sót nội dung nào đó, trình biên dịch sẽ dừng và mọi lỗi biên dịch sẽ
xuất hiện trong tab Lỗi trong cửa sổ Hộp công cụ. Khi gặp phải một danh sách dài các
lỗi biên dịch, hãy luôn bắt đầu từ lỗi đầu tiên. Bấm đúp vào lỗi trong danh sách và trình
soạn thảo sẽ chuyển trực tiếp đến dòng có lỗi. Sửa lỗi và biên dịch lại. Đôi khi một lỗi
cú pháp đơn giản sẽ dẫn đến một số lỗi không liên quan, mặc dù chỉ có lỗi đầu tiên là
hợp lệ. Đây là danh sách các lỗi biên dịch thường gặp và cách khắc phục: • Biến không
được xác định – Bạn quên khai báo một biến có kiểu dữ liệu. Nếu đó là biến toàn cục
hoặc biến ngoài, hãy khai báo nó ở đầu tệp. Nếu đó là biến cục bộ, hãy tìm lần xuất
hiện đầu tiên và đặt phần khai báo kiểu dữ liệu trước biến đó. Nếu không, hãy kiểm tra
chính tả hoặc kiểu chữ (hoa/thường) của tên biến. • Biến đã được xác định – Bạn đã
khai báo cùng một biến hai lần. Loại bỏ phần khai báo kiểu dữ liệu khỏi tất cả các khai
báo biến trùng lặp. • Hàm không được xác định – Nếu hàm được đề cập nằm trong
tệp bao gồm hoặc thư viện, hãy đảm bảo rằng lệnh #include hoặc #import được đặt ở
đầu tệp và đúng. Nếu không, hãy kiểm tra chính tả hoặc kiểu chữ của tên hàm và đảm
bảo rằng nó tồn tại trong tệp hiện tại hoặc trong tệp bao gồm hoặc thư viện có liên
quan. 144 www.ZTCprep.com Mẹo và thủ thuật • Phép gán bất hợp pháp được sử
dụng – Điều này thường liên quan đến dấu bằng (=). Hãy nhớ rằng một dấu bằng duy
nhất dành cho phép gán biến và hai dấu bằng (==) là toán tử so sánh. Sửa toán tử gán
thành toán tử so sánh thích hợp. • Phép gán dự kiến – Điều này thường liên quan đến
toán tử so sánh "bằng" (==). Bạn đã sử dụng hai dấu bằng thay vì một trong phép gán
biến. Sửa toán tử thành một dấu bằng duy nhất. • Dấu ngoặc đơn bên phải không cân
bằng – Điều này thường xảy ra trong câu lệnh if khi sử dụng dấu ngoặc đơn lồng nhau.
Đi đến dòng được chỉ ra bởi lỗi đầu tiên và chèn dấu ngoặc đơn bên trái vào vị trí thích
hợp. • Dấu ngoặc trái không cân bằng – Đây là một dấu ngoặc đơn khó. Lỗi thường
xảy ra ở cuối dòng chương trình. Về cơ bản bạn đã quên dấu ngoặc đơn ở đâu đó.
Kiểm tra kỹ mã bạn đã chỉnh sửa gần đây và tìm dấu ngoặc đơn bên phải bị thiếu. Bạn
có thể phải nhận xét các dòng mã để xác định vấn đề. • Đếm tham số sai – Bạn có
quá ít hoặc quá nhiều đối số trong một hàm. Kiểm tra kỹ cú pháp hàm trong Tham
chiếu MQL và sửa các đối số. • Cần có dấu chấm phẩy – Có thể bạn đã quên đặt dấu
chấm phẩy ở cuối dòng. Đặt dấu chấm phẩy ở cuối dòng trước. Lưu ý rằng thiếu dấu
chấm phẩy cũng có thể gây ra bất kỳ lỗi nào ở trên, vì vậy hãy nhớ đặt các dấu chấm
phẩy đó! 145 www.ZTCprep.com CHƯƠNG TRÌNH CỐ VẤN CHUYÊN GIA Chương 9
Các chỉ báo và tập lệnh tùy chỉnh Không có cuốn sách nào về MQL sẽ hoàn chỉnh nếu
không đề cập đến các chỉ báo và tập lệnh tùy chỉnh. Các chỉ báo tích hợp trong
MetaTrader khá hạn chế, nhưng may mắn thay MQL cho phép các lập trình viên tạo
các chỉ báo của riêng họ. Nếu bạn đang tìm kiếm một chỉ báo phổ biến không có trong
MT4 thì rất có thể ai đó đã tạo một chỉ báo đó. Chương này sẽ là tổng quan cơ bản về
việc tạo chỉ báo tùy chỉnh. Hầu hết các chỉ báo đều sử dụng các công thức toán học
phức tạp và đó là lĩnh vực của các lập trình viên giàu kinh nghiệm hơn. Tuy nhiên, một
chỉ báo không cần phải phức tạp. Chúng ta sẽ tạo một chỉ báo tùy chỉnh trong chương
này chỉ sử dụng một vài dòng mã. Bộ đệm Bộ đệm là mảng lưu trữ các giá trị chỉ báo
và phép tính. Một chỉ báo tùy chỉnh có thể có tối đa 8 bộ đệm. Bộ đệm sử dụng các chỉ
mục, giống như mảng và có phạm vi từ 0 đến 7. Khi bạn gọi một chỉ báo tùy chỉnh
trong chuyên gia tư vấn bằng cách sử dụng hàm iCustom(), tham số tiếp theo trong
hàm là bộ đệm chỉ báo. Để tìm vùng đệm thích hợp cho dòng chỉ báo, bạn thường
kiểm tra mã nguồn, nếu có. Nếu mã nguồn được định dạng rõ ràng với các tên biến mô
tả, bạn sẽ có thể xác định bộ đệm thích hợp khá dễ dàng. Chúng ta sẽ đề cập đến
cách đặt tên thích hợp cho bộ đệm chỉ báo trong chương này. Tạo chỉ báo tùy chỉnh
Hãy xây dựng một chỉ báo tùy chỉnh bằng cách sử dụng hai chỉ báo MetaTrader tích
hợp để tính toán các đường của chúng tôi. Chúng ta sẽ xây dựng một chỉ báo Dải
Bollinger đã được sửa đổi. Dải Bollinger bao gồm 3 đường – đường trung tâm là
đường trung bình động đơn giản, cùng với đường trên và đường dưới có giá trị được
xác định bằng độ lệch chuẩn. Chúng ta có thể tạo chỉ báo Dải bollinger của riêng mình
bằng cách sử dụng các chỉ báo Trung bình trượt và Độ lệch chuẩn. Chúng tôi muốn
tạo một chỉ báo sử dụng đường trung bình động hàm mũ để tính toán các đường, trái
ngược với đường trung bình động đơn giản. 146 www.ZTCprep.com Các chỉ báo và
tập lệnh tùy chỉnh Chúng tôi bắt đầu bằng cách sử dụng trình hướng dẫn để tạo tệp
chỉ báo của mình. Chọn Mới từ menu Tệp hoặc thanh công cụ để mở trình hướng dẫn
và tạo chỉ báo tùy chỉnh. Điền tên chỉ báo và thêm thông số nếu muốn. Trên trang cuối
cùng, chúng tôi đã thêm ba dòng chỉ báo cùng màu. Đây là kết quả của thuật sĩ. Hiện
tại chúng ta đã bỏ qua hàm start(): //+--------------------------------- --------------------------------+ //|
EMA Bollinger.mq4 | //| Andrew trẻ | //| http://www.easyexpertforex.com | //+----------------
--------------------------------------- -------------------+ #bản quyền tài sản "Andrew Young" #link tài
sản "http://www.easyexpertforex.com" #property Indicator_chart_window #property
Indicator_buffers 3 #property Indicator_color1 DeepSkyBlue #property
Indicator_color2 DeepSkyBlue #property Indicator_color3 DeepSkyBlue //---- đệm gấp
đôi ExtMapBuffer1[]; nhân đôi ExtMapBuffer2[]; nhân đôi ExtMapBuffer3[]; //+--------------
----------------------------------------- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+---
---------------------------------------------------- -------------------+ int init() { //---- chỉ báo
SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,ExtMapBuffer1);
SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,ExtMapBuffer2);
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,ExtMapBuffer3); //---- return(0); } Hãy
chuyển sự chú ý của chúng ta tới các phần tử được in đậm. Khai báo #property đặt
tham số cho bộ đệm chỉ báo của chúng tôi. Thuộc tính Indicator_chart_window vẽ chỉ
báo của chúng tôi trong cửa sổ biểu đồ chính. Nếu chúng ta đang tạo một bộ dao
động và muốn vẽ chỉ báo trong một cửa sổ riêng biệt, thì thay vào đó, chúng ta sẽ sử
dụng thuộc tính Indicator_separate_window. Thuộc tính Indicator_buffers đặt số lượng
bộ đệm cho chỉ báo của chúng tôi. Trong trường hợp này chúng tôi đang sử dụng ba
bộ đệm. Thuộc tính Indicator_color đặt màu của cả ba dòng thành DeepSkyBlue. 147
www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN NGHIỆP Tiếp theo là các khai báo
cho mảng bộ đệm của chúng ta. Chúng tôi có ba bộ đệm có tên ExtMapBuffer(1-3).
Chúng tôi sẽ sớm thay đổi các mã định danh mảng này thành một cái gì đó mang tính
mô tả hơn. Hàm init() là nơi chúng ta đặt các thuộc tính cho bộ đệm chỉ báo của mình.
SetIndexBuffer() liên kết một mảng bộ đệm với một chỉ mục bộ đệm. Chỉ mục bộ đệm
là những gì chúng tôi đề cập đến khi đặt thuộc tính cho một dòng chỉ báo cũng như
khi chúng tôi gọi một dòng chỉ báo từ EA bằng cách sử dụng hàm iCustom(). Tham số
đầu tiên là một số nguyên từ 0 đến 7 và tham số thứ hai là tên của mảng đệm. Các
thuộc tính của bản vẽ Hàm SetIndexStyle() thiết lập loại đường để vẽ, cùng với các
thuộc tính của đường đó. Mỗi dòng chỉ báo sẽ có hàm SetIndexStyle() tương ứng. Đây
là cú pháp: void SetIndexStyle(int BufferIndex, int LineType, int LineStyle = EMPTY, int
LineWidth = EMPTY, color LineColor = CLR_NONE) • BufferIndex – Chỉ mục của bộ
đệm, từ 0 đến 7. • LineType – Đặt loại bộ đệm đường để vẽ. DRAW_LINE vẽ một
đường duy nhất, DRAW_HISTOGRAM vẽ biểu đồ dọc (xem ví dụ về OsMA hoặc Bộ tạo
dao động tuyệt vời), DRAW_ARROW vẽ một biểu tượng và DRAW_NONE không vẽ
đường nào. • LineStyle – Một tham số tùy chọn cho biết kiểu vẽ. Được sử dụng chủ
yếu cho các dòng loại DRAW_LINE. Theo mặc định, một đường liền nét được vẽ
(STYLE_SOLID). Bạn cũng có thể vẽ các đường đứt nét (STYLE_DASH) và chấm
(STYLE_DOT). • LineWidth – Một tham số tùy chọn cho biết chiều rộng của đường tính
bằng pixel. Giá trị mặc định là 1. • LineColor – Một tham số tùy chọn cho biết màu của
đường. Nếu bạn sử dụng trình hướng dẫn, màu sẽ được đặt bằng cách sử dụng khai
báo #property, nhưng bạn cũng có thể đặt màu ở đây. Nếu bạn đang sử dụng
DRAW_ARROW làm LineType, hàm SetArrow() cho phép bạn đặt ký hiệu phông chữ
Wingdings để vẽ trên biểu đồ. Tham số đầu tiên là chỉ mục bộ đệm và tham số thứ hai
là hằng số nguyên biểu thị ký hiệu cần vẽ. Các ký hiệu có thể được tìm thấy trong
Tham chiếu MQL trong Hằng số tiêu chuẩn – Mã mũi tên. Bạn có thể muốn thêm mô
tả cho các dòng chỉ báo sẽ được hiển thị trong chú giải công cụ hoặc trong cửa sổ dữ
liệu. Để thực hiện việc này, hãy sử dụng hàm SetIndexLabel(). Tham số đầu tiên là chỉ
mục bộ đệm và tham số thứ hai là mô tả văn bản. Chúng tôi sẽ sớm thêm những thứ
này vào chỉ báo của chúng tôi. 148 www.ZTCprep.com Các chỉ báo và tập lệnh tùy
chỉnh Nếu chỉ báo của bạn được vẽ trong một cửa sổ riêng biệt (chẳng hạn như bộ
dao động) và bạn muốn thêm các mức để biểu thị mức quá mua hoặc quá bán (chẳng
hạn như trong các chỉ báo Stochastic hoặc RSI) hoặc mức 0 (chẳng hạn như trong chỉ
báo CCI), bạn có thể sử dụng các hàm SetLevelStyle() và SetLevelValue(). Xem Tham
chiếu MQL trong Chỉ báo tùy chỉnh để biết thêm thông tin. Bạn cũng có thể muốn chỉ
định một tên chỉ báo ngắn gọn sẽ được hiển thị ở góc trên cùng bên trái của cửa sổ
chỉ báo. Sử dụng hàm IndicatorShortName() để đặt giá trị này. Tham số duy nhất là
một chuỗi văn bản sẽ xuất hiện ở góc trên bên trái của cửa sổ chỉ báo, cũng như trong
cửa sổ dữ liệu. Sử dụng tên bộ đệm mô tả Đây là mã chỉ báo được cập nhật của chúng
tôi. Lưu ý rằng chúng tôi đã đổi tên các mảng đệm để mang tính mô tả rõ hơn về chức
năng thực tế của chúng. Chúng tôi đã thay đổi tham số thứ hai của hàm
SetIndexBuffer() để phản ánh tên bộ đệm mới. Chúng tôi cũng đã thêm
SetIndexLabel() cho mỗi dòng để hiển thị tên mô tả trong Cửa sổ dữ liệu. //---- đệm đôi
EMA[]; gấp đôi UpperBand[]; gấp đôi LowerBand[]; //+-----------------------------------------------------
-- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+------------------------------------------
------------- -------------------+ int init() { //---- chỉ báo SetIndexStyle(0,DRAW_LINE);
SetIndexBuffer(0,EMA); SetIndexLabel(0,"EMA"); SetIndexStyle(1,DRAW_LINE);
SetIndexBuffer(1,UpperBand); SetIndexLabel(1,"UpperBand");
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,LowerBand);
SetIndexLabel(2,"LowerBand"); //---- return(0); } Chúng tôi đã đổi tên các mảng vùng
đệm từ tên mặc định (ExtMapBuffer) thành tên mang tính mô tả hơn. EMA[] sẽ là vùng
đệm của chúng tôi cho đường trung tâm và UpperBand[] và LowerBand[] sẽ lần lượt là
dải trên và dải dưới. 149 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Các
hàm SetIndexBuffer() liên kết các chỉ mục bộ đệm với mảng bộ đệm của chúng ta.
EMA là 0, UpperBand là 1 và LowerBand là 2. Lưu ý rằng các dấu ngoặc nhọn bị loại
khỏi tên định danh mảng cho tham số SetIndexBuffer() thứ hai. Các hàm
SetIndexLabel() đặt tên mô tả cho từng vùng đệm chỉ báo. Trong trường hợp này, tên
dòng giống với tên định danh của chúng tôi. Chúng sẽ xuất hiện trên chú giải công cụ
chuột cũng như trong Cửa sổ dữ liệu. Nếu một lập trình viên khác quyết định sử dụng
chỉ báo này trong chuyên gia cố vấn, thì định dạng ở trên sẽ làm rõ chính xác chỉ mục
bộ đệm chỉ báo nào họ nên sử dụng cho mỗi dòng. Hàm start() của chỉ báo Trình
hướng dẫn chỉ chèn một biểu thức vào hàm start(): int count_bars =
IndicatorCounted(); IndicatorCounted() trả về số thanh trên biểu đồ mà chỉ báo đã tính
toán. Khi EA được khởi động lần đầu tiên, giá trị này sẽ là 0. Chỉ báo sẽ được tính cho
mọi thanh trên biểu đồ. Trên các thanh tiếp theo, chúng ta sẽ kiểm tra hàm
IndicatorCounted() để xem có bao nhiêu thanh đã được tính toán, từ đó chúng ta sẽ
biết chính xác có bao nhiêu thanh mới cần tính toán. Việc tính toán chỉ báo của chúng
tôi sẽ diễn ra bên trong vòng lặp for. Điểm bắt đầu sẽ là thanh chưa được tính toán
đầu tiên và điểm kết thúc sẽ là thanh hiện tại. Chúng ta sẽ so sánh giá trị của
IndicatorCounted() với biến Bars được xác định trước, biến này trả về số lượng thanh
trên biểu đồ hiện tại. Điều này sẽ xác định điểm khởi đầu của chúng tôi. Đây là mã cho
vòng lặp for: int count_bars = IndicatorCounted(); if(counted_bars > 0) count_bars--; int
CalculateBars = Thanh - count_bars; for(int Count = CalculateBars; Count >= 0; Count-
-) { // Tính toán chỉ báo } Câu lệnh if đầu tiên sẽ giảm giá trị của count_bars đi 1 khi
tính toán các thanh mới. Chúng tôi sẽ luôn tính toán ít nhất hai thanh trước đó. Điều
này là do điều kiện có thể không tính được dấu tích cuối cùng của thanh trong một số
trường hợp. Tiếp theo, chúng tôi xác định số lượng thanh cần tính toán bằng cách trừ
count_bars khỏi biến Bars được xác định trước. Điều này được lưu trữ trong biến
CalculateBars. 150 www.ZTCprep.com Chỉ báo và Tập lệnh Tùy chỉnh Trong vòng lặp
for của chúng tôi, biến tăng Count được đặt thành giá trị của CalculateBars, điều kiện
để kết thúc là khi Count nhỏ hơn 0 và biến Count được giảm dần sau mỗi lần lặp. Điều
này sẽ tính toán từng thanh trên biểu đồ từ trái sang phải. Đây là mã để tính Dải
Bollinger của chúng tôi. Chúng ta sẽ khai báo biến ngoài BandsPeriod ở đầu tệp. Vòng
lặp for là vòng lặp chúng ta đã tạo ở trên: // Các tham số bên ngoài extern int
BandsPeriod = 20; // hàm start() for(int Count = CalculateBars; Count >= 0; Count--) {
EMA[Count] = iMA(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); UpperBand[Count] = EMA[Count]
+ StdDev; LowerBand[Count] = EMA[Count] - StdDev; } Đầu tiên, chúng ta gọi chỉ báo
Trung bình trượt tích hợp bằng hàm iMA() và gán giá trị trả về cho EMA[Count]. Lưu ý
rằng chỉ mục mảng và tham số Shift cho chỉ báo trung bình động đều sử dụng giá trị
Đếm hiện tại. Tiếp theo, chúng tôi gọi chỉ báo Độ lệch chuẩn bằng iStdDev(). Để tính
dải trên, tất cả những gì chúng ta cần làm là cộng độ lệch chuẩn vào đường trung bình
động. Điều này được lưu trữ trong mảng đệm UpperBand[]. Để tính LowerBand[],
chúng tôi trừ đi độ lệch chuẩn khỏi mức trung bình động. Hãy mở rộng chỉ báo của
chúng tôi thêm một chút bằng cách cung cấp cho nó đầy đủ các cài đặt. Chúng tôi sẽ
thêm cài đặt để điều chỉnh dịch chuyển kỳ hạn, phương pháp trung bình động và các
tham số giá được áp dụng cũng như điều chỉnh độ lệch chuẩn: // Tham số bên ngoài
extern int BandsPeriod = 20; extern int BandsShift = 0; extern int BandsMethod = 1;
extern int BandsPrice = 0; extern int Độ lệch = 1; 151 www.ZTCprep.com LẬP TRÌNH
TƯ VẤN CHUYÊN NGHIỆP // start() function for(int Count = CalculateBars; Count >= 0;
Count--) { EMA[Count] =
iMA(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice ,Đếm); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice,Count);
UpperBand[Count] = EMA[Count] + (StdDev * Độ lệch); LowerBand[Count] =
EMA[Count] - (StdDev * Độ lệch); } Chúng tôi đã thêm các biến bên ngoài để điều chỉnh
các tham số còn lại cho hàm iMA() và iStdDev(). Chúng tôi cũng đã thêm một tham số
để điều chỉnh số lượng độ lệch chuẩn. Để tính toán điều này, chúng tôi chỉ cần nhân
StdDev với Độ lệch. Bây giờ chúng ta có chỉ báo Dải bollinger có thể điều chỉnh hoàn
toàn, linh hoạt hơn chỉ báo MetaTrader tiêu chuẩn. Mã đầy đủ được liệt kê trong Phụ
lục E. Bạn có thể làm được nhiều việc hơn với các chỉ báo tùy chỉnh thay vì chỉ tính
toán lại các chỉ báo tích hợp. Tùy thuộc vào trình độ kiến thức toán học của bạn, bạn
có thể mã hóa các chỉ báo không có trong MetaTrader hoặc thậm chí tạo chỉ báo của
riêng bạn. Bạn cũng có thể vẽ và thao tác các đối tượng. Nếu bạn muốn tìm hiểu thêm
về cách tạo chỉ báo tùy chỉnh, hãy xem chủ đề Tham khảo MQL Chỉ báo tùy chỉnh,
Hàm đối tượng và Toán & Lượng giác. Tập lệnh Tập lệnh là một chương trình MQL chỉ
chạy một lần khi nó được đính kèm lần đầu tiên vào biểu đồ. Tập lệnh có thể được sử
dụng để tự động hóa một loạt hành động giao dịch, chẳng hạn như đóng tất cả các
lệnh trên biểu đồ hoặc gửi lệnh chờ xử lý. Một số tập lệnh, chẳng hạn như tập lệnh
Period_converter đi kèm với MetaTrader, có thể vẽ lại biểu đồ dựa trên khoảng thời
gian tùy chỉnh. Tệp mã nguồn tập lệnh phải có chỉ thị thuộc tính show_confirm hoặc
show_inputs. Thuộc tính show_confirm nhắc người dùng xác nhận hoạt động của tập
lệnh, trong khi show_inputs hiển thị hộp thoại thuộc tính tập lệnh. #property
show_confirm // hiển thị hộp thoại xác nhận #property show_inputs // hiển thị hộp
thoại thuộc tính Nếu tập lệnh của bạn có các tham số cần điều chỉnh, hãy sử dụng
thuộc tính show_inputs. Nếu không, hãy sử dụng show_confirm. 152
www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Cũng giống như các chỉ báo và
cố vấn chuyên gia, các tập lệnh sử dụng các hàm init(), deinit() và start(). Hãy nhớ
rằng mỗi hàm sẽ chỉ được chạy một lần – init() và start() khi tập lệnh được khởi động
và deinit() khi tập lệnh bị xóa. Bạn có thể đính kèm một tập lệnh vào biểu đồ tại một
thời điểm. MetaTrader đi kèm với một số tập lệnh mẫu. Tất cả các tập lệnh được lưu
trong thư mục \experts\scripts. 153 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục A Cố vấn chuyên gia đơn giản Đây là chuyên gia cố vấn đơn
giản từ chương 2. #property bản quyền "Andrew Young" // Biến ngoài extern double
LotSize = 0.1; StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern
int Trượt = 5; extern int MagicNumber = 123; extern int FastMAPeriod = 10; extern int
SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int BánTicket; điểm sử dụng gấp
đôi; int UseTrượt trượt; // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol());
UseSlippage = GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường
trung bình động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi
SlowMA = iMA(NULL,0,SlowMAPeriod,0,0,0,0); 154 www.ZTCprep.com Phụ lục A //
Lệnh mua if(FastMA > SlowMA && BuyTicket == 0) {
OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0 &&
SellTicket > 0) { double CloseLots = OrderLots(); gấp đôi Giá đóng cửa = Hỏi; bool Đã
đóng = OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } double
OpenPrice = Hỏi; // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss
= OpenPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit =
OpenPrice + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Order",MagicNumber,0,Green); BánTicket = 0; } //
Lệnh bán if(FastMA < SlowMA && SellTicket == 0) {
OrderSelect(BuyTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0 && BuyTicket >
0) { CloseLots = OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } OpenPrice = Giá thầu;
if(StopLoss > 0) double SellStopLoss = OpenPrice + (StopLoss * UsePoint);
if(TakeProfit > 0) double SellTakeProfit = OpenPrice - (TakeProfit * UsePoint);
SellTicket = OrderSend(Symbol(),OP_SELL,LotSize,OpenPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Sell Order",MagicNumber,0,Red); MuaVé = 0; } 155
www.ZTCprep.com CHUYÊN GIA LẬP TRÌNH TƯ VẤN return(0); } // Hàm điểm Pip
nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits = MarketInfo(Tiền tệ,MODE_DIGITS);
if(CalcDigits == 2 || CalcDigits == 3) gấp đôi CalcPoint = 0,01; khác nếu(CalcDigits == 4
|| CalcDigits == 5) CalcPoint = 0,0001; return(CalcPoint); } // Lấy hàm trượt giá int
GetSlippage(string Money, int SlippagePips) { int CalcDigits =
MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 4) double
CalcSlippage = SlippagePips; else if(CalcDigits == 3 || CalcDigits == 5) CalcSlippage =
SlippagePips * 10; return(CalcSlippage); } Chuyên gia cố vấn đơn giản với các lệnh
đang chờ xử lý Đây là chuyên gia cố vấn đơn giản sử dụng các lệnh dừng đang chờ xử
lý: #property Copyright "Andrew Young" // Biến bên ngoài extern double LotSize = 0.1;
StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern int
PendingPips = 10; extern int Trượt = 5; extern int MagicNumber = 123; extern int
FastMAPeriod = 10; extern int SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int
BánTicket; điểm sử dụng gấp đôi; int UseTrượt trượt; 156 www.ZTCprep.com Phụ lục
A // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol()); UseSlippage =
GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường trung bình
động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,0); // Lệnh mua if(FastMA > SlowMA && BuyTicket ==
0) { OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0
&& SellTicket > 0 && OrderType() == OP_SELL) { double CloseLots = OrderLots(); gấp
đôi Giá đóng cửa = Hỏi; bool Đã đóng =
OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } // Xóa đơn hàng khác
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_SELLSTOP) { bool
Đã xóa = OrderDelete(SellTicket,Red); } double PendingPrice = Cao[0] + (PendingPips *
UsePoint); // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss =
PendingPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit = Đang
chờ xử lý + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Stop Order",MagicNumber,0,Green); BánTicket = 0; }
157 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA // Bán lệnh if(FastMA <
SlowMA && SellTicket == 0) { OrderSelect(BuyTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0 && BuyTicket > 0 && OrderType() == OP_BUY) { CloseLots =
OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } else
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_BUYSTOP) { Đã xóa
= OrderDelete(SellTicket,Red); } Giá đang chờ xử lý = Thấp[0] - (PendingPips *
UsePoint); if(StopLoss > 0) double SellStopLoss = Giá đang chờ xử lý + (StopLoss *
UsePoint); if(TakeProfit > 0) double SellTakeProfit = Đang chờ xử lý - (TakeProfit *
UsePoint); SellTicket =
OrderSend(Symbol(),OP_SELLSTOP,LotSize,PendingPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Lệnh dừng bán",MagicNumber,0,Red); MuaVé = 0; } trả
về(0); } // Hàm điểm Pip nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits =
MarketInfo(Tiền tệ,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 3) gấp đôi
CalcPoint = 0,01; khác nếu(CalcDigits == 4 || CalcDigits == 5) CalcPoint = 0,0001;
return(CalcPoint); } // Lấy hàm trượt giá int GetSlippage(string Money, int
SlippagePips) { int CalcDigits = MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2
|| CalcDigits == 4) double CalcSlippage = SlippagePips; else if(CalcDigits == 3 ||
CalcDigits == 5) CalcSlippage = SlippagePips * 10; return(CalcSlippage); } 158
www.ZTCprep.com Phụ lục A 159 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục B Cố vấn chuyên gia nâng cao Đây là chuyên gia cố vấn với
các tính năng nâng cao từ chương 3. #property Copyright "Andrew Young" #include
com" #property Indicator_chart_window #property Indicator_buffers 3 #property
Indicator_color1 DeepSkyBlue #property Indicator_color2 DeepSkyBlue #property
Indicator_color3 DeepSkyBlue //---- đệm đôi ExtMapBuffer1[]; double ExtMapBuffer2[];
double ExtMapBuffer3[]; //+---- -------------------------------------------------- ------------+ //| Chức năng
khởi tạo chỉ báo tùy chỉnh | //+----------------- -----------------------------------------+ int init() { //-- --
chỉ báo SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,ExtMapBuffer1);
SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,ExtMapBuffer2);
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,ExtMapBuffer3); //--- - return(0); } Hãy
chú ý đến các phần tử được liệt kê in đậm. Khai báo #property đặt tham số cho vùng
đệm chỉ báo của chúng ta. Thuộc tính Indicator_chart_window vẽ chỉ báo của chúng ta
trong cửa sổ biểu đồ chính. Nếu chúng ta đang tạo một bộ dao động và muốn vẽ chỉ
báo trong một cửa sổ riêng biệt, thay vào đó, chúng tôi sẽ sử dụng thuộc tính
Indicator_separate_window. Thuộc tính Indicator_buffers đặt số lượng bộ đệm cho chỉ
báo của chúng tôi. Trong trường hợp này chúng tôi đang sử dụng ba bộ đệm. Thuộc
tính Indicator_color đặt màu của cả ba dòng thành DeepSkyBlue. 147
www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN NGHIỆP Tiếp theo là các khai báo
cho mảng bộ đệm của chúng ta. Chúng tôi có ba bộ đệm có tên ExtMapBuffer(1-3).
Chúng tôi sẽ sớm thay đổi các mã định danh mảng này thành một cái gì đó mang tính
mô tả hơn. Hàm init() là nơi chúng ta đặt các thuộc tính cho bộ đệm chỉ báo của mình.
SetIndexBuffer() liên kết một mảng bộ đệm với một chỉ mục bộ đệm. Chỉ mục bộ đệm
là những gì chúng tôi đề cập đến khi đặt thuộc tính cho một dòng chỉ báo cũng như
khi chúng tôi gọi một dòng chỉ báo từ EA bằng cách sử dụng hàm iCustom(). Tham số
đầu tiên là một số nguyên từ 0 đến 7 và tham số thứ hai là tên của mảng đệm. Các
thuộc tính của bản vẽ Hàm SetIndexStyle() thiết lập loại đường để vẽ, cùng với các
thuộc tính của đường đó. Mỗi dòng chỉ báo sẽ có hàm SetIndexStyle() tương ứng. Đây
là cú pháp: void SetIndexStyle(int BufferIndex, int LineType, int LineStyle = EMPTY, int
LineWidth = EMPTY, color LineColor = CLR_NONE) • BufferIndex – Chỉ mục của bộ
đệm, từ 0 đến 7. • LineType – Đặt loại bộ đệm đường để vẽ. DRAW_LINE vẽ một
đường duy nhất, DRAW_HISTOGRAM vẽ biểu đồ dọc (xem ví dụ về OsMA hoặc Bộ tạo
dao động tuyệt vời), DRAW_ARROW vẽ một biểu tượng và DRAW_NONE không vẽ
đường nào. • LineStyle – Một tham số tùy chọn cho biết kiểu vẽ. Được sử dụng chủ
yếu cho các dòng loại DRAW_LINE. Theo mặc định, một đường liền nét được vẽ
(STYLE_SOLID). Bạn cũng có thể vẽ các đường đứt nét (STYLE_DASH) và chấm
(STYLE_DOT). • LineWidth – Một tham số tùy chọn cho biết chiều rộng của đường tính
bằng pixel. Giá trị mặc định là 1. • LineColor – Một tham số tùy chọn cho biết màu của
đường. Nếu bạn sử dụng trình hướng dẫn, màu sẽ được đặt bằng cách sử dụng khai
báo #property, nhưng bạn cũng có thể đặt màu ở đây. Nếu bạn đang sử dụng
DRAW_ARROW làm LineType, hàm SetArrow() cho phép bạn đặt ký hiệu phông chữ
Wingdings để vẽ trên biểu đồ. Tham số đầu tiên là chỉ mục bộ đệm và tham số thứ hai
là hằng số nguyên biểu thị ký hiệu cần vẽ. Các ký hiệu có thể được tìm thấy trong
Tham chiếu MQL trong Hằng số tiêu chuẩn – Mã mũi tên. Bạn có thể muốn thêm mô
tả cho các dòng chỉ báo sẽ được hiển thị trong chú giải công cụ hoặc trong cửa sổ dữ
liệu. Để thực hiện việc này, hãy sử dụng hàm SetIndexLabel(). Tham số đầu tiên là chỉ
mục bộ đệm và tham số thứ hai là mô tả văn bản. Chúng tôi sẽ sớm thêm những thứ
này vào chỉ báo của chúng tôi. 148 www.ZTCprep.com Các chỉ báo và tập lệnh tùy
chỉnh Nếu chỉ báo của bạn được vẽ trong một cửa sổ riêng biệt (chẳng hạn như bộ
dao động) và bạn muốn thêm các mức để biểu thị mức quá mua hoặc quá bán (chẳng
hạn như trong các chỉ báo Stochastic hoặc RSI) hoặc mức 0 (chẳng hạn như trong chỉ
báo CCI), bạn có thể sử dụng các hàm SetLevelStyle() và SetLevelValue(). Xem Tham
chiếu MQL trong Chỉ báo tùy chỉnh để biết thêm thông tin. Bạn cũng có thể muốn chỉ
định một tên chỉ báo ngắn gọn sẽ được hiển thị ở góc trên cùng bên trái của cửa sổ
chỉ báo. Sử dụng hàm IndicatorShortName() để đặt giá trị này. Tham số duy nhất là
một chuỗi văn bản sẽ xuất hiện ở góc trên bên trái của cửa sổ chỉ báo, cũng như trong
cửa sổ dữ liệu. Sử dụng tên bộ đệm mô tả Đây là mã chỉ báo được cập nhật của chúng
tôi. Lưu ý rằng chúng tôi đã đổi tên các mảng đệm để mang tính mô tả rõ hơn về chức
năng thực tế của chúng. Chúng tôi đã thay đổi tham số thứ hai của hàm
SetIndexBuffer() để phản ánh tên bộ đệm mới. Chúng tôi cũng đã thêm
SetIndexLabel() cho mỗi dòng để hiển thị tên mô tả trong Cửa sổ dữ liệu. //---- đệm đôi
EMA[]; gấp đôi UpperBand[]; gấp đôi LowerBand[]; //+-----------------------------------------------------
-- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+------------------------------------------
------------- -------------------+ int init() { //---- chỉ báo SetIndexStyle(0,DRAW_LINE);
SetIndexBuffer(0,EMA); SetIndexLabel(0,"EMA"); SetIndexStyle(1,DRAW_LINE);
SetIndexBuffer(1,UpperBand); SetIndexLabel(1,"UpperBand");
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,LowerBand);
SetIndexLabel(2,"LowerBand"); //---- return(0); } Chúng tôi đã đổi tên các mảng vùng
đệm từ tên mặc định (ExtMapBuffer) thành tên mang tính mô tả hơn. EMA[] sẽ là vùng
đệm của chúng tôi cho đường trung tâm và UpperBand[] và LowerBand[] sẽ lần lượt là
dải trên và dải dưới. 149 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Các
hàm SetIndexBuffer() liên kết các chỉ mục bộ đệm với mảng bộ đệm của chúng ta.
EMA là 0, UpperBand là 1 và LowerBand là 2. Lưu ý rằng các dấu ngoặc nhọn bị loại
khỏi tên định danh mảng cho tham số SetIndexBuffer() thứ hai. Các hàm
SetIndexLabel() đặt tên mô tả cho từng vùng đệm chỉ báo. Trong trường hợp này, tên
dòng giống với tên định danh của chúng tôi. Chúng sẽ xuất hiện trên chú giải công cụ
chuột cũng như trong Cửa sổ dữ liệu. Nếu một lập trình viên khác quyết định sử dụng
chỉ báo này trong chuyên gia cố vấn, thì định dạng ở trên sẽ làm rõ chính xác chỉ mục
bộ đệm chỉ báo nào họ nên sử dụng cho mỗi dòng. Hàm start() của chỉ báo Trình
hướng dẫn chỉ chèn một biểu thức vào hàm start(): int count_bars =
IndicatorCounted(); IndicatorCounted() trả về số thanh trên biểu đồ mà chỉ báo đã tính
toán. Khi EA được khởi động lần đầu tiên, giá trị này sẽ là 0. Chỉ báo sẽ được tính cho
mọi thanh trên biểu đồ. Trên các thanh tiếp theo, chúng ta sẽ kiểm tra hàm
IndicatorCounted() để xem có bao nhiêu thanh đã được tính toán, từ đó chúng ta sẽ
biết chính xác có bao nhiêu thanh mới cần tính toán. Việc tính toán chỉ báo của chúng
tôi sẽ diễn ra bên trong vòng lặp for. Điểm bắt đầu sẽ là thanh chưa được tính toán
đầu tiên và điểm kết thúc sẽ là thanh hiện tại. Chúng ta sẽ so sánh giá trị của
IndicatorCounted() với biến Bars được xác định trước, biến này trả về số lượng thanh
trên biểu đồ hiện tại. Điều này sẽ xác định điểm khởi đầu của chúng tôi. Đây là mã cho
vòng lặp for: int count_bars = IndicatorCounted(); if(counted_bars > 0) count_bars--; int
CalculateBars = Thanh - count_bars; for(int Count = CalculateBars; Count >= 0; Count-
-) { // Tính toán chỉ báo } Câu lệnh if đầu tiên sẽ giảm giá trị của count_bars đi 1 khi
tính toán các thanh mới. Chúng tôi sẽ luôn tính toán ít nhất hai thanh trước đó. Điều
này là do điều kiện có thể không tính được dấu tích cuối cùng của thanh trong một số
trường hợp. Tiếp theo, chúng tôi xác định số lượng thanh cần tính toán bằng cách trừ
count_bars khỏi biến Bars được xác định trước. Điều này được lưu trữ trong biến
CalculateBars. 150 www.ZTCprep.com Chỉ báo và Tập lệnh Tùy chỉnh Trong vòng lặp
for của chúng tôi, biến tăng Count được đặt thành giá trị của CalculateBars, điều kiện
để kết thúc là khi Count nhỏ hơn 0 và biến Count được giảm dần sau mỗi lần lặp. Điều
này sẽ tính toán từng thanh trên biểu đồ từ trái sang phải. Đây là mã để tính Dải
Bollinger của chúng tôi. Chúng ta sẽ khai báo biến ngoài BandsPeriod ở đầu tệp. Vòng
lặp for là vòng lặp chúng ta đã tạo ở trên: // Các tham số bên ngoài extern int
BandsPeriod = 20; // hàm start() for(int Count = CalculateBars; Count >= 0; Count--) {
EMA[Count] = iMA(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); UpperBand[Count] = EMA[Count]
+ StdDev; LowerBand[Count] = EMA[Count] - StdDev; } Đầu tiên, chúng ta gọi chỉ báo
Trung bình trượt tích hợp bằng hàm iMA() và gán giá trị trả về cho EMA[Count]. Lưu ý
rằng chỉ mục mảng và tham số Shift cho chỉ báo trung bình động đều sử dụng giá trị
Đếm hiện tại. Tiếp theo, chúng tôi gọi chỉ báo Độ lệch chuẩn bằng iStdDev(). Để tính
dải trên, tất cả những gì chúng ta cần làm là cộng độ lệch chuẩn vào đường trung bình
động. Điều này được lưu trữ trong mảng đệm UpperBand[]. Để tính LowerBand[],
chúng tôi trừ đi độ lệch chuẩn khỏi mức trung bình động. Hãy mở rộng chỉ báo của
chúng tôi thêm một chút bằng cách cung cấp cho nó đầy đủ các cài đặt. Chúng tôi sẽ
thêm cài đặt để điều chỉnh dịch chuyển kỳ hạn, phương pháp trung bình động và các
tham số giá được áp dụng cũng như điều chỉnh độ lệch chuẩn: // Tham số bên ngoài
extern int BandsPeriod = 20; extern int BandsShift = 0; extern int BandsMethod = 1;
extern int BandsPrice = 0; extern int Độ lệch = 1; 151 www.ZTCprep.com LẬP TRÌNH
TƯ VẤN CHUYÊN NGHIỆP // start() function for(int Count = CalculateBars; Count >= 0;
Count--) { EMA[Count] =
iMA(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice ,Đếm); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice,Count);
UpperBand[Count] = EMA[Count] + (StdDev * Độ lệch); LowerBand[Count] =
EMA[Count] - (StdDev * Độ lệch); } Chúng tôi đã thêm các biến bên ngoài để điều chỉnh
các tham số còn lại cho hàm iMA() và iStdDev(). Chúng tôi cũng đã thêm một tham số
để điều chỉnh số lượng độ lệch chuẩn. Để tính toán điều này, chúng tôi chỉ cần nhân
StdDev với Độ lệch. Bây giờ chúng ta có chỉ báo Dải bollinger có thể điều chỉnh hoàn
toàn, linh hoạt hơn chỉ báo MetaTrader tiêu chuẩn. Mã đầy đủ được liệt kê trong Phụ
lục E. Bạn có thể làm được nhiều việc hơn với các chỉ báo tùy chỉnh thay vì chỉ tính
toán lại các chỉ báo tích hợp. Tùy thuộc vào trình độ kiến thức toán học của bạn, bạn
có thể mã hóa các chỉ báo không có trong MetaTrader hoặc thậm chí tạo chỉ báo của
riêng bạn. Bạn cũng có thể vẽ và thao tác các đối tượng. Nếu bạn muốn tìm hiểu thêm
về cách tạo chỉ báo tùy chỉnh, hãy xem chủ đề Tham khảo MQL Chỉ báo tùy chỉnh,
Hàm đối tượng và Toán & Lượng giác. Tập lệnh Tập lệnh là một chương trình MQL chỉ
chạy một lần khi nó được đính kèm lần đầu tiên vào biểu đồ. Tập lệnh có thể được sử
dụng để tự động hóa một loạt hành động giao dịch, chẳng hạn như đóng tất cả các
lệnh trên biểu đồ hoặc gửi lệnh chờ xử lý. Một số tập lệnh, chẳng hạn như tập lệnh
Period_converter đi kèm với MetaTrader, có thể vẽ lại biểu đồ dựa trên khoảng thời
gian tùy chỉnh. Tệp mã nguồn tập lệnh phải có chỉ thị thuộc tính show_confirm hoặc
show_inputs. Thuộc tính show_confirm nhắc người dùng xác nhận hoạt động của tập
lệnh, trong khi show_inputs hiển thị hộp thoại thuộc tính tập lệnh. #property
show_confirm // hiển thị hộp thoại xác nhận #property show_inputs // hiển thị hộp
thoại thuộc tính Nếu tập lệnh của bạn có các tham số cần được điều chỉnh, hãy sử
dụng thuộc tính show_inputs. Nếu không, hãy sử dụng show_confirm. 152
www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Cũng giống như các chỉ báo và
cố vấn chuyên gia, các tập lệnh sử dụng các hàm init(), deinit() và start(). Hãy nhớ
rằng mỗi hàm sẽ chỉ được chạy một lần – init() và start() khi tập lệnh được khởi động
và deinit() khi tập lệnh bị xóa. Bạn có thể đính kèm một tập lệnh vào biểu đồ tại một
thời điểm. MetaTrader đi kèm với một số tập lệnh mẫu. Tất cả các tập lệnh được lưu
trong thư mục \experts\scripts. 153 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục A Cố vấn chuyên gia đơn giản Đây là chuyên gia cố vấn đơn
giản từ chương 2. #property bản quyền "Andrew Young" // Biến ngoài extern double
LotSize = 0.1; StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern
int Trượt = 5; extern int MagicNumber = 123; extern int FastMAPeriod = 10; extern int
SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int BánTicket; điểm sử dụng gấp
đôi; int UseTrượt trượt; // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol());
UseSlippage = GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường
trung bình động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi
SlowMA = iMA(NULL,0,SlowMAPeriod,0,0,0,0); 154 www.ZTCprep.com Phụ lục A //
Lệnh mua if(FastMA > SlowMA && BuyTicket == 0) {
OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0 &&
SellTicket > 0) { double CloseLots = OrderLots(); gấp đôi Giá đóng cửa = Hỏi; bool Đã
đóng = OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } double
OpenPrice = Hỏi; // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss
= OpenPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit =
OpenPrice + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Order",MagicNumber,0,Green); BánTicket = 0; } //
Lệnh bán if(FastMA < SlowMA && SellTicket == 0) {
OrderSelect(BuyTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0 && BuyTicket >
0) { CloseLots = OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } OpenPrice = Giá thầu;
if(StopLoss > 0) double SellStopLoss = OpenPrice + (StopLoss * UsePoint);
if(TakeProfit > 0) double SellTakeProfit = OpenPrice - (TakeProfit * UsePoint);
SellTicket = OrderSend(Symbol(),OP_SELL,LotSize,OpenPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Sell Order",MagicNumber,0,Red); MuaVé = 0; } 155
www.ZTCprep.com CHUYÊN GIA LẬP TRÌNH TƯ VẤN return(0); } // Hàm điểm Pip
nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits = MarketInfo(Tiền tệ,MODE_DIGITS);
if(CalcDigits == 2 || CalcDigits == 3) gấp đôi CalcPoint = 0,01; khác nếu(CalcDigits == 4
|| CalcDigits == 5) CalcPoint = 0,0001; return(CalcPoint); } // Lấy hàm trượt giá int
GetSlippage(string Money, int SlippagePips) { int CalcDigits =
MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 4) double
CalcSlippage = SlippagePips; else if(CalcDigits == 3 || CalcDigits == 5) CalcSlippage =
SlippagePips * 10; return(CalcSlippage); } Chuyên gia cố vấn đơn giản với các lệnh
đang chờ xử lý Đây là chuyên gia cố vấn đơn giản sử dụng các lệnh dừng đang chờ xử
lý: #property Copyright "Andrew Young" // Biến bên ngoài extern double LotSize = 0.1;
StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern int
PendingPips = 10; extern int Trượt = 5; extern int MagicNumber = 123; extern int
FastMAPeriod = 10; extern int SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int
BánTicket; điểm sử dụng gấp đôi; int UseTrượt trượt; 156 www.ZTCprep.com Phụ lục
A // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol()); UseSlippage =
GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường trung bình
động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,0); // Lệnh mua if(FastMA > SlowMA && BuyTicket ==
0) { OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0
&& SellTicket > 0 && OrderType() == OP_SELL) { double CloseLots = OrderLots(); gấp
đôi Giá đóng cửa = Hỏi; bool Đã đóng =
OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } // Xóa đơn hàng khác
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_SELLSTOP) { bool
Đã xóa = OrderDelete(SellTicket,Red); } double PendingPrice = Cao[0] + (PendingPips *
UsePoint); // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss =
PendingPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit = Đang
chờ xử lý + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Stop Order",MagicNumber,0,Green); BánTicket = 0; }
157 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA // Bán lệnh if(FastMA <
SlowMA && SellTicket == 0) { OrderSelect(BuyTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0 && BuyTicket > 0 && OrderType() == OP_BUY) { CloseLots =
OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } else
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_BUYSTOP) { Đã xóa
= OrderDelete(SellTicket,Red); } Giá đang chờ xử lý = Thấp[0] - (PendingPips *
UsePoint); if(StopLoss > 0) double SellStopLoss = Giá đang chờ xử lý + (StopLoss *
UsePoint); if(TakeProfit > 0) double SellTakeProfit = Đang chờ xử lý - (TakeProfit *
UsePoint); SellTicket =
OrderSend(Symbol(),OP_SELLSTOP,LotSize,PendingPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Lệnh dừng bán",MagicNumber,0,Red); MuaVé = 0; } trả
về(0); } // Hàm điểm Pip nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits =
MarketInfo(Tiền tệ,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 3) gấp đôi
CalcPoint = 0,01; khác nếu(CalcDigits == 4 || CalcDigits == 5) CalcPoint = 0,0001;
return(CalcPoint); } // Lấy hàm trượt giá int GetSlippage(string Money, int
SlippagePips) { int CalcDigits = MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2
|| CalcDigits == 4) double CalcSlippage = SlippagePips; else if(CalcDigits == 3 ||
CalcDigits == 5) CalcSlippage = SlippagePips * 10; return(CalcSlippage); } 158
www.ZTCprep.com Phụ lục A 159 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục B Cố vấn chuyên gia nâng cao Đây là chuyên gia cố vấn với
các tính năng nâng cao từ chương 3. #property Copyright "Andrew Young" #include
com" #property Indicator_chart_window #property Indicator_buffers 3 #property
Indicator_color1 DeepSkyBlue #property Indicator_color2 DeepSkyBlue #property
Indicator_color3 DeepSkyBlue //---- đệm đôi ExtMapBuffer1[]; double ExtMapBuffer2[];
double ExtMapBuffer3[]; //+---- -------------------------------------------------- ------------+ //| Chức năng
khởi tạo chỉ báo tùy chỉnh | //+----------------- -----------------------------------------+ int init() { //-- --
chỉ báo SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,ExtMapBuffer1);
SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,ExtMapBuffer2);
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,ExtMapBuffer3); //--- - return(0); } Hãy
chú ý đến các phần tử được liệt kê in đậm. Khai báo #property đặt tham số cho vùng
đệm chỉ báo của chúng ta. Thuộc tính Indicator_chart_window vẽ chỉ báo của chúng ta
trong cửa sổ biểu đồ chính. Nếu chúng ta đang tạo một bộ dao động và muốn vẽ chỉ
báo trong một cửa sổ riêng biệt, thay vào đó, chúng tôi sẽ sử dụng thuộc tính
Indicator_separate_window. Thuộc tính Indicator_buffers đặt số lượng bộ đệm cho chỉ
báo của chúng tôi. Trong trường hợp này chúng tôi đang sử dụng ba bộ đệm. Thuộc
tính Indicator_color đặt màu của cả ba dòng thành DeepSkyBlue. 147
www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN NGHIỆP Tiếp theo là các khai báo
cho mảng bộ đệm của chúng ta. Chúng tôi có ba bộ đệm có tên ExtMapBuffer(1-3).
Chúng tôi sẽ sớm thay đổi các mã định danh mảng này thành một cái gì đó mang tính
mô tả hơn. Hàm init() là nơi chúng ta đặt các thuộc tính cho bộ đệm chỉ báo của mình.
SetIndexBuffer() liên kết một mảng bộ đệm với một chỉ mục bộ đệm. Chỉ mục bộ đệm
là những gì chúng tôi đề cập đến khi đặt thuộc tính cho một dòng chỉ báo cũng như
khi chúng tôi gọi một dòng chỉ báo từ EA bằng cách sử dụng hàm iCustom(). Tham số
đầu tiên là một số nguyên từ 0 đến 7 và tham số thứ hai là tên của mảng đệm. Các
thuộc tính của bản vẽ Hàm SetIndexStyle() thiết lập loại đường để vẽ, cùng với các
thuộc tính của đường đó. Mỗi dòng chỉ báo sẽ có hàm SetIndexStyle() tương ứng. Đây
là cú pháp: void SetIndexStyle(int BufferIndex, int LineType, int LineStyle = EMPTY, int
LineWidth = EMPTY, color LineColor = CLR_NONE) • BufferIndex – Chỉ mục của bộ
đệm, từ 0 đến 7. • LineType – Đặt loại bộ đệm đường để vẽ. DRAW_LINE vẽ một
đường duy nhất, DRAW_HISTOGRAM vẽ biểu đồ dọc (xem ví dụ về OsMA hoặc Bộ tạo
dao động tuyệt vời), DRAW_ARROW vẽ một biểu tượng và DRAW_NONE không vẽ
đường nào. • LineStyle – Một tham số tùy chọn cho biết kiểu vẽ. Được sử dụng chủ
yếu cho các dòng loại DRAW_LINE. Theo mặc định, một đường liền nét được vẽ
(STYLE_SOLID). Bạn cũng có thể vẽ các đường đứt nét (STYLE_DASH) và chấm
(STYLE_DOT). • LineWidth – Một tham số tùy chọn cho biết chiều rộng của đường tính
bằng pixel. Giá trị mặc định là 1. • LineColor – Một tham số tùy chọn cho biết màu của
đường. Nếu bạn sử dụng trình hướng dẫn, màu sẽ được đặt bằng cách sử dụng khai
báo #property, nhưng bạn cũng có thể đặt màu ở đây. Nếu bạn đang sử dụng
DRAW_ARROW làm LineType, hàm SetArrow() cho phép bạn đặt ký hiệu phông chữ
Wingdings để vẽ trên biểu đồ. Tham số đầu tiên là chỉ mục bộ đệm và tham số thứ hai
là hằng số nguyên biểu thị ký hiệu cần vẽ. Các ký hiệu có thể được tìm thấy trong
Tham chiếu MQL trong Hằng số tiêu chuẩn – Mã mũi tên. Bạn có thể muốn thêm mô
tả cho các dòng chỉ báo sẽ được hiển thị trong chú giải công cụ hoặc trong cửa sổ dữ
liệu. Để thực hiện việc này, hãy sử dụng hàm SetIndexLabel(). Tham số đầu tiên là chỉ
mục bộ đệm và tham số thứ hai là mô tả văn bản. Chúng tôi sẽ sớm thêm những thứ
này vào chỉ báo của chúng tôi. 148 www.ZTCprep.com Các chỉ báo và tập lệnh tùy
chỉnh Nếu chỉ báo của bạn được vẽ trong một cửa sổ riêng biệt (chẳng hạn như bộ
dao động) và bạn muốn thêm các mức để biểu thị mức quá mua hoặc quá bán (chẳng
hạn như trong các chỉ báo Stochastic hoặc RSI) hoặc mức 0 (chẳng hạn như trong chỉ
báo CCI), bạn có thể sử dụng các hàm SetLevelStyle() và SetLevelValue(). Xem Tham
chiếu MQL trong Chỉ báo tùy chỉnh để biết thêm thông tin. Bạn cũng có thể muốn chỉ
định một tên chỉ báo ngắn gọn sẽ được hiển thị ở góc trên cùng bên trái của cửa sổ
chỉ báo. Sử dụng hàm IndicatorShortName() để đặt giá trị này. Tham số duy nhất là
một chuỗi văn bản sẽ xuất hiện ở góc trên bên trái của cửa sổ chỉ báo, cũng như trong
cửa sổ dữ liệu. Sử dụng tên bộ đệm mô tả Đây là mã chỉ báo được cập nhật của chúng
tôi. Lưu ý rằng chúng tôi đã đổi tên các mảng đệm để mang tính mô tả rõ hơn về chức
năng thực tế của chúng. Chúng tôi đã thay đổi tham số thứ hai của hàm
SetIndexBuffer() để phản ánh tên bộ đệm mới. Chúng tôi cũng đã thêm
SetIndexLabel() cho mỗi dòng để hiển thị tên mô tả trong Cửa sổ dữ liệu. //---- đệm đôi
EMA[]; gấp đôi UpperBand[]; gấp đôi LowerBand[]; //+-----------------------------------------------------
-- -------------------+ //| Chức năng khởi tạo chỉ báo tùy chỉnh | //+------------------------------------------
------------- -------------------+ int init() { //---- chỉ báo SetIndexStyle(0,DRAW_LINE);
SetIndexBuffer(0,EMA); SetIndexLabel(0,"EMA"); SetIndexStyle(1,DRAW_LINE);
SetIndexBuffer(1,UpperBand); SetIndexLabel(1,"UpperBand");
SetIndexStyle(2,DRAW_LINE); SetIndexBuffer(2,LowerBand);
SetIndexLabel(2,"LowerBand"); //---- return(0); } Chúng tôi đã đổi tên các mảng vùng
đệm từ tên mặc định (ExtMapBuffer) thành tên mang tính mô tả hơn. EMA[] sẽ là vùng
đệm của chúng tôi cho đường trung tâm và UpperBand[] và LowerBand[] sẽ lần lượt là
dải trên và dải dưới. 149 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Các
hàm SetIndexBuffer() liên kết các chỉ mục bộ đệm với mảng bộ đệm của chúng ta.
EMA là 0, UpperBand là 1 và LowerBand là 2. Lưu ý rằng các dấu ngoặc nhọn bị loại
khỏi tên định danh mảng cho tham số SetIndexBuffer() thứ hai. Các hàm
SetIndexLabel() đặt tên mô tả cho từng vùng đệm chỉ báo. Trong trường hợp này, tên
dòng giống với tên định danh của chúng tôi. Chúng sẽ xuất hiện trên chú giải công cụ
chuột cũng như trong Cửa sổ dữ liệu. Nếu một lập trình viên khác quyết định sử dụng
chỉ báo này trong chuyên gia cố vấn, thì định dạng ở trên sẽ làm rõ chính xác chỉ mục
bộ đệm chỉ báo nào họ nên sử dụng cho mỗi dòng. Hàm start() của chỉ báo Trình
hướng dẫn chỉ chèn một biểu thức vào hàm start(): int count_bars =
IndicatorCounted(); IndicatorCounted() trả về số thanh trên biểu đồ mà chỉ báo đã tính
toán. Khi EA được khởi động lần đầu tiên, giá trị này sẽ là 0. Chỉ báo sẽ được tính cho
mọi thanh trên biểu đồ. Trên các thanh tiếp theo, chúng ta sẽ kiểm tra hàm
IndicatorCounted() để xem có bao nhiêu thanh đã được tính toán, từ đó chúng ta sẽ
biết chính xác có bao nhiêu thanh mới cần tính toán. Việc tính toán chỉ báo của chúng
tôi sẽ diễn ra bên trong vòng lặp for. Điểm bắt đầu sẽ là thanh chưa được tính toán
đầu tiên và điểm kết thúc sẽ là thanh hiện tại. Chúng ta sẽ so sánh giá trị của
IndicatorCounted() với biến Bars được xác định trước, biến này trả về số lượng thanh
trên biểu đồ hiện tại. Điều này sẽ xác định điểm khởi đầu của chúng tôi. Đây là mã cho
vòng lặp for: int count_bars = IndicatorCounted(); if(counted_bars > 0) count_bars--; int
CalculateBars = Thanh - count_bars; for(int Count = CalculateBars; Count >= 0; Count-
-) { // Tính toán chỉ báo } Câu lệnh if đầu tiên sẽ giảm giá trị của count_bars đi 1 khi
tính toán các thanh mới. Chúng tôi sẽ luôn tính toán ít nhất hai thanh trước đó. Điều
này là do điều kiện có thể không tính được dấu tích cuối cùng của thanh trong một số
trường hợp. Tiếp theo, chúng tôi xác định số lượng thanh cần tính toán bằng cách trừ
count_bars khỏi biến Bars được xác định trước. Điều này được lưu trữ trong biến
CalculateBars. 150 www.ZTCprep.com Chỉ báo và Tập lệnh Tùy chỉnh Trong vòng lặp
for của chúng tôi, biến tăng Count được đặt thành giá trị của CalculateBars, điều kiện
để kết thúc là khi Count nhỏ hơn 0 và biến Count được giảm dần sau mỗi lần lặp. Điều
này sẽ tính toán từng thanh trên biểu đồ từ trái sang phải. Đây là mã để tính Dải
Bollinger của chúng tôi. Chúng ta sẽ khai báo biến ngoài BandsPeriod ở đầu tệp. Vòng
lặp for là vòng lặp chúng ta đã tạo ở trên: // Các tham số bên ngoài extern int
BandsPeriod = 20; // hàm start() for(int Count = CalculateBars; Count >= 0; Count--) {
EMA[Count] = iMA(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,0,MODE_EMA,0,Count); UpperBand[Count] = EMA[Count]
+ StdDev; LowerBand[Count] = EMA[Count] - StdDev; } Đầu tiên, chúng ta gọi chỉ báo
Trung bình trượt tích hợp bằng hàm iMA() và gán giá trị trả về cho EMA[Count]. Lưu ý
rằng chỉ mục mảng và tham số Shift cho chỉ báo trung bình động đều sử dụng giá trị
Đếm hiện tại. Tiếp theo, chúng tôi gọi chỉ báo Độ lệch chuẩn bằng iStdDev(). Để tính
dải trên, tất cả những gì chúng ta cần làm là cộng độ lệch chuẩn vào đường trung bình
động. Điều này được lưu trữ trong mảng đệm UpperBand[]. Để tính LowerBand[],
chúng tôi trừ đi độ lệch chuẩn khỏi mức trung bình động. Hãy mở rộng chỉ báo của
chúng tôi thêm một chút bằng cách cung cấp cho nó đầy đủ các cài đặt. Chúng tôi sẽ
thêm cài đặt để điều chỉnh dịch chuyển kỳ hạn, phương pháp trung bình động và các
tham số giá được áp dụng cũng như điều chỉnh độ lệch chuẩn: // Tham số bên ngoài
extern int BandsPeriod = 20; extern int BandsShift = 0; extern int BandsMethod = 1;
extern int BandsPrice = 0; extern int Độ lệch = 1; 151 www.ZTCprep.com LẬP TRÌNH
TƯ VẤN CHUYÊN NGHIỆP // start() function for(int Count = CalculateBars; Count >= 0;
Count--) { EMA[Count] =
iMA(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice ,Đếm); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice,Count);
UpperBand[Count] = EMA[Count] + (StdDev * Độ lệch); LowerBand[Count] =
EMA[Count] - (StdDev * Độ lệch); } Chúng tôi đã thêm các biến bên ngoài để điều chỉnh
các tham số còn lại cho hàm iMA() và iStdDev(). Chúng tôi cũng đã thêm một tham số
để điều chỉnh số lượng độ lệch chuẩn. Để tính toán điều này, chúng tôi chỉ cần nhân
StdDev với Độ lệch. Bây giờ chúng ta có chỉ báo Dải bollinger có thể điều chỉnh hoàn
toàn, linh hoạt hơn chỉ báo MetaTrader tiêu chuẩn. Mã đầy đủ được liệt kê trong Phụ
lục E. Bạn có thể làm được nhiều việc hơn với các chỉ báo tùy chỉnh thay vì chỉ tính
toán lại các chỉ báo tích hợp. Tùy thuộc vào trình độ kiến thức toán học của bạn, bạn
có thể mã hóa các chỉ báo không có trong MetaTrader hoặc thậm chí tạo chỉ báo của
riêng bạn. Bạn cũng có thể vẽ và thao tác các đối tượng. Nếu bạn muốn tìm hiểu thêm
về cách tạo chỉ báo tùy chỉnh, hãy xem chủ đề Tham khảo MQL Chỉ báo tùy chỉnh,
Hàm đối tượng và Toán & Lượng giác. Tập lệnh Tập lệnh là một chương trình MQL chỉ
chạy một lần khi nó được đính kèm lần đầu tiên vào biểu đồ. Tập lệnh có thể được sử
dụng để tự động hóa một loạt hành động giao dịch, chẳng hạn như đóng tất cả các
lệnh trên biểu đồ hoặc gửi lệnh chờ xử lý. Một số tập lệnh, chẳng hạn như tập lệnh
Period_converter đi kèm với MetaTrader, có thể vẽ lại biểu đồ dựa trên khoảng thời
gian tùy chỉnh. Tệp mã nguồn tập lệnh phải có chỉ thị thuộc tính show_confirm hoặc
show_inputs. Thuộc tính show_confirm nhắc người dùng xác nhận hoạt động của tập
lệnh, trong khi show_inputs hiển thị hộp thoại thuộc tính tập lệnh. #property
show_confirm // hiển thị hộp thoại xác nhận #property show_inputs // hiển thị hộp
thoại thuộc tính Nếu tập lệnh của bạn có các tham số cần được điều chỉnh, hãy sử
dụng thuộc tính show_inputs. Nếu không, hãy sử dụng show_confirm. 152
www.ZTCprep.com Các chỉ báo và tập lệnh tùy chỉnh Cũng giống như các chỉ báo và
cố vấn chuyên gia, các tập lệnh sử dụng các hàm init(), deinit() và start(). Hãy nhớ
rằng mỗi hàm sẽ chỉ được chạy một lần – init() và start() khi tập lệnh được khởi động
và deinit() khi tập lệnh bị xóa. Bạn có thể đính kèm một tập lệnh vào biểu đồ tại một
thời điểm. MetaTrader đi kèm với một số tập lệnh mẫu. Tất cả các tập lệnh được lưu
trong thư mục \experts\scripts. 153 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục A Cố vấn chuyên gia đơn giản Đây là chuyên gia cố vấn đơn
giản từ chương 2. #property bản quyền "Andrew Young" // Biến ngoài extern double
LotSize = 0.1; StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern
int Trượt = 5; extern int MagicNumber = 123; extern int FastMAPeriod = 10; extern int
SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int BánTicket; điểm sử dụng gấp
đôi; int UseTrượt trượt; // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol());
UseSlippage = GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường
trung bình động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi
SlowMA = iMA(NULL,0,SlowMAPeriod,0,0,0,0); 154 www.ZTCprep.com Phụ lục A //
Lệnh mua if(FastMA > SlowMA && BuyTicket == 0) {
OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0 &&
SellTicket > 0) { double CloseLots = OrderLots(); gấp đôi Giá đóng cửa = Hỏi; bool Đã
đóng = OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } double
OpenPrice = Hỏi; // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss
= OpenPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit =
OpenPrice + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Order",MagicNumber,0,Green); BánTicket = 0; } //
Lệnh bán if(FastMA < SlowMA && SellTicket == 0) {
OrderSelect(BuyTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0 && BuyTicket >
0) { CloseLots = OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } OpenPrice = Giá thầu;
if(StopLoss > 0) double SellStopLoss = OpenPrice + (StopLoss * UsePoint);
if(TakeProfit > 0) double SellTakeProfit = OpenPrice - (TakeProfit * UsePoint);
SellTicket = OrderSend(Symbol(),OP_SELL,LotSize,OpenPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Sell Order",MagicNumber,0,Red); MuaVé = 0; } 155
www.ZTCprep.com CHUYÊN GIA LẬP TRÌNH TƯ VẤN return(0); } // Hàm điểm Pip
nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits = MarketInfo(Tiền tệ,MODE_DIGITS);
if(CalcDigits == 2 || CalcDigits == 3) gấp đôi CalcPoint = 0,01; khác nếu(CalcDigits == 4
|| CalcDigits == 5) CalcPoint = 0,0001; return(CalcPoint); } // Lấy hàm trượt giá int
GetSlippage(string Money, int SlippagePips) { int CalcDigits =
MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 4) double
CalcSlippage = SlippagePips; else if(CalcDigits == 3 || CalcDigits == 5) CalcSlippage =
SlippagePips * 10; return(CalcSlippage); } Chuyên gia cố vấn đơn giản với các lệnh
đang chờ xử lý Đây là chuyên gia cố vấn đơn giản sử dụng các lệnh dừng đang chờ xử
lý: #property Copyright "Andrew Young" // Biến bên ngoài extern double LotSize = 0.1;
StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern int
PendingPips = 10; extern int Trượt = 5; extern int MagicNumber = 123; extern int
FastMAPeriod = 10; extern int SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int
BánTicket; điểm sử dụng gấp đôi; int UseTrượt trượt; 156 www.ZTCprep.com Phụ lục
A // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol()); UseSlippage =
GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường trung bình
động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,0); // Lệnh mua if(FastMA > SlowMA && BuyTicket ==
0) { OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0
&& SellTicket > 0 && OrderType() == OP_SELL) { double CloseLots = OrderLots(); gấp
đôi Giá đóng cửa = Hỏi; bool Đã đóng =
OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } // Xóa đơn hàng khác
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_SELLSTOP) { bool
Đã xóa = OrderDelete(SellTicket,Red); } double PendingPrice = Cao[0] + (PendingPips *
UsePoint); // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss =
PendingPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit = Đang
chờ xử lý + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Stop Order",MagicNumber,0,Green); BánTicket = 0; }
157 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA // Bán lệnh if(FastMA <
SlowMA && SellTicket == 0) { OrderSelect(BuyTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0 && BuyTicket > 0 && OrderType() == OP_BUY) { CloseLots =
OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } else
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_BUYSTOP) { Đã xóa
= OrderDelete(SellTicket,Red); } Giá đang chờ xử lý = Thấp[0] - (PendingPips *
UsePoint); if(StopLoss > 0) double SellStopLoss = Giá đang chờ xử lý + (StopLoss *
UsePoint); if(TakeProfit > 0) double SellTakeProfit = Đang chờ xử lý - (TakeProfit *
UsePoint); SellTicket =
OrderSend(Symbol(),OP_SELLSTOP,LotSize,PendingPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Lệnh dừng bán",MagicNumber,0,Red); MuaVé = 0; } trả
về(0); } // Hàm điểm Pip nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits =
MarketInfo(Tiền tệ,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 3) gấp đôi
CalcPoint = 0,01; khác nếu(CalcDigits == 4 || CalcDigits == 5) CalcPoint = 0,0001;
return(CalcPoint); } // Lấy hàm trượt giá int GetSlippage(string Money, int
SlippagePips) { int CalcDigits = MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2
|| CalcDigits == 4) double CalcSlippage = SlippagePips; else if(CalcDigits == 3 ||
CalcDigits == 5) CalcSlippage = SlippagePips * 10; return(CalcSlippage); } 158
www.ZTCprep.com Phụ lục A 159 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục B Cố vấn chuyên gia nâng cao Đây là chuyên gia cố vấn với
các tính năng nâng cao từ chương 3. #property Copyright "Andrew Young" #include
#property bản quyền "Andrew Young" // Biến ngoài extern double LotSize = 0,1;
StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern int Trượt = 5;
extern int MagicNumber = 123; extern int FastMAPeriod = 10; extern int SlowMAPeriod
= 20; // Biến toàn cục int BuyTicket; int BánTicket; điểm sử dụng gấp đôi; int UseTrượt
trượt; // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol()); UseSlippage =
GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường trung bình
động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,0); 154 www.ZTCprep.com Phụ lục A // Lệnh mua
if(FastMA > SlowMA && BuyTicket == 0) { OrderSelect(SellTicket,SELECT_BY_TICKET);
// Đóng lệnh if(OrderCloseTime() == 0 && SellTicket > 0) { double CloseLots =
OrderLots(); gấp đôi Giá đóng cửa = Hỏi; bool Đã đóng =
OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } double OpenPrice =
Hỏi; // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss = OpenPrice
- (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit = OpenPrice +
(TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Order",MagicNumber,0,Green); BánTicket = 0; } //
Lệnh bán if(FastMA < SlowMA && SellTicket == 0) {
OrderSelect(BuyTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0 && BuyTicket >
0) { CloseLots = OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } OpenPrice = Giá thầu;
if(StopLoss > 0) double SellStopLoss = OpenPrice + (StopLoss * UsePoint);
if(TakeProfit > 0) double SellTakeProfit = OpenPrice - (TakeProfit * UsePoint);
SellTicket = OrderSend(Symbol(),OP_SELL,LotSize,OpenPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Sell Order",MagicNumber,0,Red); MuaVé = 0; } 155
www.ZTCprep.com CHUYÊN GIA LẬP TRÌNH TƯ VẤN return(0); } // Hàm điểm Pip
nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits = MarketInfo(Tiền tệ,MODE_DIGITS);
if(CalcDigits == 2 || CalcDigits == 3) gấp đôi CalcPoint = 0,01; khác nếu(CalcDigits == 4
|| CalcDigits == 5) CalcPoint = 0,0001; return(CalcPoint); } // Lấy hàm trượt giá int
GetSlippage(string Money, int SlippagePips) { int CalcDigits =
MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 4) double
CalcSlippage = SlippagePips; else if(CalcDigits == 3 || CalcDigits == 5) CalcSlippage =
SlippagePips * 10; return(CalcSlippage); } Chuyên gia cố vấn đơn giản với các lệnh
đang chờ xử lý Đây là chuyên gia cố vấn đơn giản sử dụng các lệnh dừng đang chờ xử
lý: #property Copyright "Andrew Young" // Biến bên ngoài extern double LotSize = 0.1;
StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern int
PendingPips = 10; extern int Trượt = 5; extern int MagicNumber = 123; extern int
FastMAPeriod = 10; extern int SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int
BánTicket; điểm sử dụng gấp đôi; int UseTrượt trượt; 156 www.ZTCprep.com Phụ lục
A // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol()); UseSlippage =
GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường trung bình
động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,0); // Lệnh mua if(FastMA > SlowMA && BuyTicket ==
0) { OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0
&& SellTicket > 0 && OrderType() == OP_SELL) { double CloseLots = OrderLots(); gấp
đôi Giá đóng cửa = Hỏi; bool Đã đóng =
OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } // Xóa đơn hàng khác
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_SELLSTOP) { bool
Đã xóa = OrderDelete(SellTicket,Red); } double PendingPrice = Cao[0] + (PendingPips *
UsePoint); // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss =
PendingPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit = Đang
chờ xử lý + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Stop Order",MagicNumber,0,Green); BánTicket = 0; }
157 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA // Bán lệnh if(FastMA <
SlowMA && SellTicket == 0) { OrderSelect(BuyTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0 && BuyTicket > 0 && OrderType() == OP_BUY) { CloseLots =
OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } else
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_BUYSTOP) { Đã xóa
= OrderDelete(SellTicket,Red); } Giá đang chờ xử lý = Thấp[0] - (PendingPips *
UsePoint); if(StopLoss > 0) double SellStopLoss = Giá đang chờ xử lý + (StopLoss *
UsePoint); if(TakeProfit > 0) double SellTakeProfit = Đang chờ xử lý - (TakeProfit *
UsePoint); SellTicket =
OrderSend(Symbol(),OP_SELLSTOP,LotSize,PendingPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Lệnh dừng bán",MagicNumber,0,Red); MuaVé = 0; } trả
về(0); } // Hàm điểm Pip nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits =
MarketInfo(Tiền tệ,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 3) gấp đôi
CalcPoint = 0,01; khác nếu(CalcDigits == 4 || CalcDigits == 5) CalcPoint = 0,0001;
return(CalcPoint); } // Lấy hàm trượt giá int GetSlippage(string Money, int
SlippagePips) { int CalcDigits = MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2
|| CalcDigits == 4) double CalcSlippage = SlippagePips; else if(CalcDigits == 3 ||
CalcDigits == 5) CalcSlippage = SlippagePips * 10; return(CalcSlippage); } 158
www.ZTCprep.com Phụ lục A 159 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục B Cố vấn chuyên gia nâng cao Đây là chuyên gia cố vấn với
các tính năng nâng cao từ chương 3. #property Copyright "Andrew Young" #include
#property bản quyền "Andrew Young" // Biến ngoài extern double LotSize = 0,1;
StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern int Trượt = 5;
extern int MagicNumber = 123; extern int FastMAPeriod = 10; extern int SlowMAPeriod
= 20; // Biến toàn cục int BuyTicket; int BánTicket; điểm sử dụng gấp đôi; int UseTrượt
trượt; // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol()); UseSlippage =
GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường trung bình
động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,0); 154 www.ZTCprep.com Phụ lục A // Lệnh mua
if(FastMA > SlowMA && BuyTicket == 0) { OrderSelect(SellTicket,SELECT_BY_TICKET);
// Đóng lệnh if(OrderCloseTime() == 0 && SellTicket > 0) { double CloseLots =
OrderLots(); gấp đôi Giá đóng cửa = Hỏi; bool Đã đóng =
OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } double OpenPrice =
Hỏi; // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss = OpenPrice
- (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit = OpenPrice +
(TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUY,LotSize,OpenPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Order",MagicNumber,0,Green); BánTicket = 0; } //
Lệnh bán if(FastMA < SlowMA && SellTicket == 0) {
OrderSelect(BuyTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0 && BuyTicket >
0) { CloseLots = OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } OpenPrice = Giá thầu;
if(StopLoss > 0) double SellStopLoss = OpenPrice + (StopLoss * UsePoint);
if(TakeProfit > 0) double SellTakeProfit = OpenPrice - (TakeProfit * UsePoint);
SellTicket = OrderSend(Symbol(),OP_SELL,LotSize,OpenPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Sell Order",MagicNumber,0,Red); MuaVé = 0; } 155
www.ZTCprep.com CHUYÊN GIA LẬP TRÌNH TƯ VẤN return(0); } // Hàm điểm Pip
nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits = MarketInfo(Tiền tệ,MODE_DIGITS);
if(CalcDigits == 2 || CalcDigits == 3) gấp đôi CalcPoint = 0,01; khác nếu(CalcDigits == 4
|| CalcDigits == 5) CalcPoint = 0,0001; return(CalcPoint); } // Lấy hàm trượt giá int
GetSlippage(string Money, int SlippagePips) { int CalcDigits =
MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 4) double
CalcSlippage = SlippagePips; else if(CalcDigits == 3 || CalcDigits == 5) CalcSlippage =
SlippagePips * 10; return(CalcSlippage); } Chuyên gia cố vấn đơn giản với các lệnh
đang chờ xử lý Đây là chuyên gia cố vấn đơn giản sử dụng các lệnh dừng đang chờ xử
lý: #property Copyright "Andrew Young" // Biến bên ngoài extern double LotSize = 0.1;
StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern int
PendingPips = 10; extern int Trượt = 5; extern int MagicNumber = 123; extern int
FastMAPeriod = 10; extern int SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int
BánTicket; điểm sử dụng gấp đôi; int UseTrượt trượt; 156 www.ZTCprep.com Phụ lục
A // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol()); UseSlippage =
GetSlippage(Symbol(),Slippage); } // Start function int start() { // Đường trung bình
động gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,0); // Lệnh mua if(FastMA > SlowMA && BuyTicket ==
0) { OrderSelect(SellTicket,SELECT_BY_TICKET); // Đóng lệnh if(OrderCloseTime() == 0
&& SellTicket > 0 && OrderType() == OP_SELL) { double CloseLots = OrderLots(); gấp
đôi Giá đóng cửa = Hỏi; bool Đã đóng =
OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); } // Xóa đơn hàng khác
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_SELLSTOP) { bool
Đã xóa = OrderDelete(SellTicket,Red); } double PendingPrice = Cao[0] + (PendingPips *
UsePoint); // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0) double BuyStopLoss =
PendingPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit = Đang
chờ xử lý + (TakeProfit * UsePoint); // Mở lệnh mua BuyTicket =
OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Buy Stop Order",MagicNumber,0,Green); BánTicket = 0; }
157 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA // Bán lệnh if(FastMA <
SlowMA && SellTicket == 0) { OrderSelect(BuyTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0 && BuyTicket > 0 && OrderType() == OP_BUY) { CloseLots =
OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); } else
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_BUYSTOP) { Đã xóa
= OrderDelete(SellTicket,Red); } Giá đang chờ xử lý = Thấp[0] - (PendingPips *
UsePoint); if(StopLoss > 0) double SellStopLoss = Giá đang chờ xử lý + (StopLoss *
UsePoint); if(TakeProfit > 0) double SellTakeProfit = Đang chờ xử lý - (TakeProfit *
UsePoint); SellTicket =
OrderSend(Symbol(),OP_SELLSTOP,LotSize,PendingPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Lệnh dừng bán",MagicNumber,0,Red); MuaVé = 0; } trả
về(0); } // Hàm điểm Pip nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits =
MarketInfo(Tiền tệ,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 3) gấp đôi
CalcPoint = 0,01; khác nếu(CalcDigits == 4 || CalcDigits == 5) CalcPoint = 0,0001;
return(CalcPoint); } // Lấy hàm trượt giá int GetSlippage(string Money, int
SlippagePips) { int CalcDigits = MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2
|| CalcDigits == 4) double CalcSlippage = SlippagePips; else if(CalcDigits == 3 ||
CalcDigits == 5) CalcSlippage = SlippagePips * 10; return(CalcSlippage); } 158
www.ZTCprep.com Phụ lục A 159 www.ZTCprep.com LẬP TRÌNH CHUYÊN GIA CỐ
VẤN CHUYÊN GIA Phụ lục B Cố vấn chuyên gia nâng cao Đây là chuyên gia cố vấn với
các tính năng nâng cao từ chương 3. #property Copyright "Andrew Young" #include //
Biến ngoài extern bool DynamicLotSize = true; bên ngoài gấp đôi EquityPercent = 2;
bên ngoài gấp đôi FixLotSize = 0,1; StopLoss kép bên ngoài = 50; bên ngoài gấp đôi
TakeProfit = 100; extern int Trượt = 5; extern int MagicNumber = 123; extern int
FastMAPeriod = 10; extern int SlowMAPeriod = 20; // Biến toàn cục int BuyTicket; int
BánTicket; điểm sử dụng gấp đôi; int UseTrượt trượt; int Mã lỗi; // Hàm khởi tạo int
init() { UsePoint = PipPoint(Symbol()); UseSlippage = GetSlippage(Symbol(),Slippage);
} // Start function int start() { // Đường trung bình động gấp đôi FastMA =
iMA(NULL,0,FastMAPeriod,0,0,0,1); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,1); 160 www.ZTCprep.com Phụ lục B // Tính toán
kích thước lô if(DynamicLotSize == true) { double RiskAmount = AccountEquity() *
(EquityPercent / 100); double TickValue = MarketInfo(Symbol(),MODE_TICKVALUE);
if(Điểm == 0,001 || Điểm == 0,00001) TickValue *= 10; gấp đôi CalcLots = (RiskAmount
/ StopLoss) / TickValue; gấp đôi Kích thước lô = CalcLots; } else LotSize = FixLotSize;
// Xác minh kích thước lô if(LotSize < MarketInfo(Symbol(),MODE_MINLOT)) { LotSize
= MarketInfo(Symbol(),MODE_MINLOT); } else if(LotSize >
MarketInfo(Symbol(),MODE_MAXLOT)) { LotSize =
MarketInfo(Symbol(),MODE_MAXLOT); } if(MarketInfo(Symbol(),MODE_LOTSTEP) ==
0,1) { LotSize = NormalizeDouble(LotSize,1); } else LotSize =
NormalizeDouble(LotSize,2); // Lệnh mua if(FastMA > SlowMA && BuyTicket == 0) { //
Đóng lệnh OrderSelect(SellTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0 &&
SellTicket > 0) { double CloseLots = OrderLots(); while(IsTradeContextBusy()) Ngủ(10);
RefreshRates(); gấp đôi Giá đóng cửa = Hỏi; bool Đã đóng =
OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); // Xử lý lỗi if(Closed ==
false) { ErrorCode = GetLastError(); chuỗi ErrDesc = ErrorDescription(ErrorCode); 161
www.ZTCprep.com Chuỗi LẬP TRÌNH TƯ VẤN CHUYÊN GIA ErrAlert =
StringConcatenate("Đóng lệnh bán - Lỗi ", ErrorCode,": ",ErrDesc); Cảnh báo(ErrAlert);
chuỗi ErrLog = StringConcatenate("Hỏi: ",Hỏi," Lô: ",LotSize, " Vé: ",SellTicket);
In(ErrLog); } } // Mở lệnh mua while(IsTradeContextBusy()) Sleep(10); RefreshRates();
MuaTicket = OrderSend(Symbol(),OP_BUY,LotSize,Ask,UseSlippage,0,0, "Mua
hàng",MagicNumber,0,Green); // Xử lý lỗi if(BuyTicket == -1) { ErrorCode =
GetLastError(); ErrDesc = Mô tả lỗi (Mã lỗi); ErrAlert = StringConcatenate("Mở lệnh
mua - Lỗi", ErrorCode, ": ",ErrDesc); Cảnh báo(ErrAlert); ErrLog =
StringConcatenate("Hỏi: ",Hỏi," Rất nhiều: ",LotSize); In(ErrLog); } // Sửa đổi đơn hàng
khác { OrderSelect(BuyTicket,SELECT_BY_TICKET); gấp đôi OpenPrice =
OrderOpenPrice(); // Tính mức dừng gấp đôi StopLevel =
MarketInfo(Symbol(),MODE_STOPLEVEL) * Điểm; RefreshRates(); gấp đôi
UpperStopLevel = Hỏi + StopLevel; gấp đôi LowerStopLevel = Giá thầu - StopLevel;
nhân đôi MinStop = 5 * UsePoint; // Tính điểm dừng lỗ và chốt lãi if(StopLoss > 0)
double BuyStopLoss = OpenPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double
BuyTakeProfit = OpenPrice + (TakeProfit * UsePoint); 162 www.ZTCprep.com Phụ lục
B // Xác minh mức dừng lỗ và chốt lãi if(BuyStopLoss > 0 && BuyStopLoss >
LowerStopLevel) { BuyStopLoss = LowerStopLevel - MinStop; } if(BuyTakeProfit > 0 &&
BuyTakeProfit < UpperStopLevel) { BuyTakeProfit = UpperStopLevel + MinStop; } // Sửa
lệnh if(IsTradeContextBusy()) Sleep(10); if(BuyStopLoss > 0 || BuyTakeProfit > 0) { bool
TicketMod = OrderModify(BuyTicket,OpenPrice,BuyStopLoss, BuyTakeProfit,0); // Xử lý
lỗi if(TicketMod == false) { ErrorCode = GetLastError(); ErrDesc = Mô tả lỗi (Mã lỗi);
ErrAlert = StringConcatenate("Sửa đổi đơn đặt hàng - Lỗi", ErrorCode, ": ",ErrDesc);
Cảnh báo(ErrAlert); ErrLog = StringConcatenate("Hỏi: ",Hỏi," Giá thầu: ",Giá thầu," Vé: ",
BuyTicket," Dừng: ",BuyStopLoss," Lợi nhuận: ",BuyTakeProfit); In(ErrLog); } } }
BánTicket = 0; } // Lệnh bán if(FastMA < SlowMA && SellTicket == 0) {
OrderSelect(BuyTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0 && BuyTicket >
0) { CloseLots = OrderLots(); while(IsTradeContextBusy()) Ngủ(10); RefreshRates();
163 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA ClosePrice = Bid; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); // Xử lý lỗi if(Closed ==
false) { ErrorCode = GetLastError(); ErrDesc = Mô tả lỗi (Mã lỗi); ErrAlert =
StringConcatenate("Đóng lệnh mua - Lỗi",ErrorCode, ": ",ErrDesc); Cảnh báo(ErrAlert);
ErrLog = StringConcatenate("Giá thầu: ",Giá thầu," Lô: ",LotSize," Vé: ", MuaTicket);
In(ErrLog); } } while(IsTradeContextBusy()) Ngủ(10); RefreshRates(); SellTicket =
OrderSend(Symbol(),OP_SELL,LotSize,Bid,UseSlippage,0,0, "Sell
Order",MagicNumber,0,Red); // Xử lý lỗi if(SellTicket == -1) { ErrorCode =
GetLastError(); ErrDesc = Mô tả lỗi (Mã lỗi); ErrAlert = StringConcatenate("Mở lệnh bán
- Lỗi", ErrorCode, ": ",ErrDesc); Cảnh báo(ErrAlert); ErrLog = StringConcatenate("Giá
thầu: ",Giá thầu," Lô: ",LotSize); In(ErrLog); } else {
OrderSelect(SellTicket,SELECT_BY_TICKET); OpenPrice = OrderOpenPrice(); StopLevel
= MarketInfo(Symbol(),MODE_STOPLEVEL) * Điểm; RefreshRates(); UpperStopLevel =
Hỏi + StopLevel; LowerStopLevel = Giá thầu - StopLevel; MinStop = 5 * Điểm sử dụng;
if(StopLoss > 0) double SellStopLoss = OpenPrice + (StopLoss * UsePoint); 164
www.ZTCprep.com Phụ lục B if(TakeProfit > 0) double SellTakeProfit = OpenPrice -
(TakeProfit * UsePoint); if(SellStopLoss > 0 && SellStopLoss < UpperStopLevel) {
SellStopLoss = UpperStopLevel + MinStop; } if(SellTakeProfit > 0 && SellTakeProfit >
LowerStopLevel) { SellTakeProfit = LowerStopLevel - MinStop; }
if(IsTradeContextBusy()) Ngủ(10); if(SellStopLoss > 0 || SellTakeProfit > 0) { TicketMod
= OrderModify(SellTicket,OpenPrice,SellStopLoss, SellTakeProfit,0); // Xử lý lỗi
if(TicketMod == false) { ErrorCode = GetLastError(); ErrDesc = Mô tả lỗi (Mã lỗi);
ErrAlert = StringConcatenate("Sửa lệnh bán - Lỗi", ErrorCode,": ",ErrDesc); Cảnh
báo(ErrAlert); ErrLog = StringConcatenate("Hỏi: ",Hỏi," Giá thầu: ",Giá thầu," Vé: ",
SellTicket," Dừng: ",SellStopLoss," Lợi nhuận: ",SellTakeProfit); In(ErrLog); } } } MuaVé =
0; } trả về(0); } // Hàm điểm Pip nhân đôi PipPoint(chuỗi Tiền tệ) { int CalcDigits =
MarketInfo(Tiền tệ,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 3) gấp đôi
CalcPoint = 0,01; khác nếu(CalcDigits == 4 || CalcDigits == 5) CalcPoint = 0,0001;
return(CalcPoint); } 165 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA // Lấy
hàm trượt giá int GetSlippage(string Money, int SlippagePips) { int CalcDigits =
MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 4) double
CalcSlippage = SlippagePips; else if(CalcDigits == 3 || CalcDigits == 5) CalcSlippage =
SlippagePips * 10; return(CalcSlippage); } Chuyên gia cố vấn nâng cao với các lệnh
đang chờ xử lý Đây là chuyên gia cố vấn nâng cao sử dụng các lệnh dừng đang chờ xử
lý: #include // Biến ngoài extern int PendingPips = 20; bên ngoài gấp đôi LotSize = 0,1;
StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100; extern int Trượt = 5;
extern int MagicNumber = 123; extern int FastMAPeriod = 10; extern int SlowMAPeriod
= 20; // Biến toàn cục int BuyTicket; int BánTicket; điểm sử dụng gấp đôi; int UseTrượt
trượt; int Mã Lỗi; // Hàm khởi tạo int init() { UsePoint = PipPoint(Symbol());
UseSlippage = GetSlippage(Symbol(),Slippage); } // Hàm bắt đầu int start() { // Trung
bình di chuyển gấp đôi FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,0); gấp đôi SlowMA
= iMA(NULL,0,SlowMAPeriod,0,0,0,0); 166 www.ZTCprep.com Phụ lục B // Lệnh mua
if(FastMA > SlowMA && BuyTicket == 0) { // Đóng lệnh
OrderSelect(SellTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0 && SellTicket >
0 && OrderType() == OP_SELL) { double CloseLots = OrderLots();
while(IsTradeContextBusy()) Ngủ(10); RefreshRates(); gấp đôi Giá đóng cửa = Hỏi;
bool Đã đóng = OrderClose(SellTicket,CloseLots,ClosePrice,UseSlippage,Red); // Xử lý
lỗi if(Closed == false) { ErrorCode = GetLastError(); chuỗi ErrDesc =
ErrorDescription(ErrorCode); string ErrAlert = StringConcatenate("Đóng lệnh bán - Lỗi",
ErrorCode,": ",ErrDesc); Cảnh báo(ErrAlert); chuỗi ErrLog = StringConcatenate("Hỏi:
",Hỏi," Lô: ",LotSize, " Vé: ",SellTicket); In(ErrLog); } } // Xóa đơn hàng else
if(OrderCloseTime() == 0 && SellTicket > 0 && OrderType() == OP_SELLSTOP) { bool
Đã xóa = OrderDelete(SellTicket,Red); if(Đã xóa == true) SellTicket = 0; // Xử lý lỗi
if(Deleted == false) { ErrorCode = GetLastError(); ErrDesc = Mô tả lỗi (Mã lỗi); ErrAlert =
StringConcatenate("Xóa lệnh dừng bán - Lỗi", ErrorCode,": ",ErrDesc); Cảnh
báo(ErrAlert); ErrLog = StringConcatenate("Hỏi: ",Hỏi," Vé: ",SellTicket); In(ErrLog); } }
167 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA // Tính toán mức dừng gấp
đôi StopLevel = MarketInfo(Symbol(),MODE_STOPLEVEL) * Điểm; RefreshRates(); gấp
đôi UpperStopLevel = Hỏi + StopLevel; nhân đôi MinStop = 5 * UsePoint; // Tính giá
chờ xử lý gấp đôi PendingPrice = High[0] + (PendingPips * UsePoint); if(PendingPrice <
UpperStopLevel) PendingPrice = UpperStopLevel + MinStop; // Tính điểm dừng lỗ và
chốt lãi if(StopLoss > 0) double BuyStopLoss = PendingPrice - (StopLoss * UsePoint);
if(TakeProfit > 0) double BuyTakeProfit = Đang chờ xử lý + (TakeProfit * UsePoint); //
Xác minh mức dừng lỗ và chốt lãi UpperStopLevel = PendingPrice + StopLevel; gấp đôi
LowerStopLevel = Giá đang chờ xử lý - StopLevel; if(BuyStopLoss > 0 && BuyStopLoss
> LowerStopLevel) { BuyStopLoss = LowerStopLevel - MinStop; } if(BuyTakeProfit > 0
&& BuyTakeProfit < UpperStopLevel) { BuyTakeProfit = UpperStopLevel + MinStop; } //
Đặt lệnh chờ if(IsTradeContextBusy()) Sleep(10); BuyTicket =
OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Lệnh dừng mua",MagicNumber,0,Green); // Xử lý lỗi
if(BuyTicket == -1) { ErrorCode = GetLastError(); ErrDesc = Mô tả lỗi (Mã lỗi); ErrAlert =
StringConcatenate("Mở lệnh dừng mua - Lỗi", ErrorCode, ": ",ErrDesc); Cảnh
báo(ErrAlert); ErrLog = StringConcatenate("Hỏi: ",Hỏi," Lô: ",LotSize," Giá: ",
PendingPrice," Dừng: ",BuyStopLoss," Lợi nhuận: ",BuyTakeProfit); In(ErrLog); }
BánTicket = 0; } 168 www.ZTCprep.com Phụ lục B // Lệnh bán if(FastMA < SlowMA &&
SellTicket == 0) { OrderSelect(BuyTicket,SELECT_BY_TICKET); if(OrderCloseTime() ==
0 && BuyTicket > 0 && OrderType() == OP_BUY) { CloseLots = OrderLots();
while(IsTradeContextBusy()) Ngủ(10); RefreshRates(); Giá đóng cửa = Giá thầu; Đã
đóng = OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); if(Đóng ==
false) { ErrorCode = GetLastError(); ErrDesc = Mô tả lỗi (Mã lỗi); ErrAlert =
StringConcatenate("Đóng lệnh mua - Lỗi",ErrorCode, ": ",ErrDesc); Cảnh báo(ErrAlert);
ErrLog = StringConcatenate("Giá thầu: ",Giá thầu," Lô: ",LotSize," Vé: ", MuaTicket);
In(ErrLog); } } else if(OrderCloseTime() == 0 && BuyTicket > 0 && OrderType() ==
OP_BUYSTOP) { while(IsTradeContextBusy()) Sleep(10); Đã đóng =
OrderDelete(MuaTicket,Red); if(Đã xóa == false) { ErrorCode = GetLastError(); ErrDesc
= Mô tả lỗi (Mã lỗi); ErrAlert = StringConcatenate("Xóa lệnh dừng mua - Lỗi",
ErrorCode,": ",ErrDesc); Cảnh báo(ErrAlert); ErrLog = StringConcatenate("Giá thầu: ",Giá
thầu," Vé: ",MuaTicket); In(ErrLog); } } StopLevel =
MarketInfo(Symbol(),MODE_STOPLEVEL) * Điểm; RefreshRates(); LowerStopLevel =
Giá thầu - StopLevel; 169 www.ZTCprep.com CHUYÊN GIA CỐ VẤN LẬP TRÌNH
MinStop = 5 * UsePoint; Giá đang chờ xử lý = Thấp[0] - (PendingPips * UsePoint);
if(PendingPrice > LowerStopLevel) PendingPrice = LowerStopLevel - MinStop;
if(StopLoss > 0) double SellStopLoss = Giá đang chờ xử lý + (StopLoss * UsePoint);
if(TakeProfit > 0) double SellTakeProfit = Đang chờ xử lý - (TakeProfit *UsePoint);
UpperStopLevel = Giá đang chờ xử lý + Mức dừng; LowerStopLevel = Giá đang chờ xử
lý - StopLevel; if(SellStopLoss > 0 && SellStopLoss < UpperStopLevel) { SellStopLoss =
UpperStopLevel + MinStop; } if(SellTakeProfit > 0 && SellTakeProfit > LowerStopLevel)
{ SellTakeProfit = LowerStopLevel - MinStop; } if(IsTradeContextBusy()) Ngủ(10);
SellTicket = OrderSend(Symbol(),OP_SELLSTOP,LotSize,PendingPrice,UseSlippage,
SellStopLoss,SellTakeProfit,"Lệnh dừng bán",MagicNumber,0,Red); if(SellTicket == -1) {
ErrorCode = GetLastError(); ErrDesc = Mô tả lỗi (Mã lỗi); ErrAlert =
StringConcatenate("Mở lệnh dừng bán - Lỗi", ErrorCode, ": ",ErrDesc); Cảnh
báo(ErrAlert); ErrLog = StringConcatenate("Giá thầu: ",Giá thầu," Lô: ",LotSize," Giá: ",
PendingPrice," Dừng: ",SellStopLoss," Lợi nhuận: ",SellTakeProfit); In(ErrLog); } MuaVé =
0; } trả về(0); } 170 www.ZTCprep.com Phụ lục B // Hàm Pip Point nhân đôi
PipPoint(chuỗi Tiền tệ) { int CalcDigits = MarketInfo(Tiền tệ,MODE_DIGITS);
if(CalcDigits == 2 || CalcDigits == 3) gấp đôi CalcPoint = 0,01; khác nếu(CalcDigits == 4
|| CalcDigits == 5) CalcPoint = 0,0001; return(CalcPoint); } // Lấy hàm trượt giá int
GetSlippage(string Money, int SlippagePips) { int CalcDigits =
MarketInfo(Currency,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 4) double
CalcSlippage = SlippagePips; else if(CalcDigits == 3 || CalcDigits == 5) CalcSlippage =
SlippagePips * 10; return(CalcSlippage); } 171 www.ZTCprep.com LẬP TRÌNH
CHUYÊN GIA CỐ VẤN CHUYÊN GIA Phụ lục C Cố vấn chuyên gia với các chức năng
Đây là cố vấn chuyên gia sử dụng các chức năng được giới thiệu trong chương 4.
Chúng tôi đã thêm chức năng "đóng tất cả các lệnh" và chức năng dừng theo dõi từ
chương 5 và Tính năng "thực thi một lần trên mỗi thanh" từ chương 7. Các hàm được
xác định trong Bao gồmExample.mqh, nội dung của chúng được liệt kê trong Phụ lục
D. // Bộ tiền xử lý #property bản quyền "Andrew Young" #include // Biến ngoài extern
bool DynamicLotSize = true; bên ngoài gấp đôi EquityPercent = 2; bên ngoài gấp đôi
FixLotSize = 0,1; StopLoss kép bên ngoài = 50; bên ngoài gấp đôi TakeProfit = 100;
extern int TrailingStop = 50; extern int Lợi nhuận tối thiểu = 50; extern int Trượt = 5;
extern int MagicNumber = 123; extern int FastMAPeriod = 10; extern int SlowMAPeriod
= 20; bool bên ngoài CheckOncePerBar = true; // Biến toàn cục int BuyTicket; int
BánTicket; điểm sử dụng gấp đôi; int UseTrượt trượt; ngày giờ CurrentTimeStamp; 172
www.ZTCprep.com Phụ lục C // Hàm init int init() { UsePoint = PipPoint(Symbol());
UseSlippage = GetSlippage(Symbol(),Slippage); } // Bắt đầu hàm int start() { // Thực thi
trên thanh mở if(CheckOncePerBar == true) { int BarShift = 1; if(CurrentTimeStamp !=
Time[0]) { CurrentTimeStamp = Time[0]; bool NewBar = đúng; } khác NewBar = false; }
khác { NewBar = true; BarShift = 0; } // Đường trung bình động gấp đôi FastMA =
iMA(NULL,0,FastMAPeriod,0,0,0,BarShift); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,BarShift); nhân đôi LastFastMA =
iMA(NULL,0,FastMAPeriod,0,0,0,BarShift+1); nhân đôi LastSlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,BarShift+1); // Tính kích thước lô double LotSize =
CalcLotSize(DynamicLotSize,EquityPercent,StopLoss,FixedLotSize); LotSize =
VerifyLotSize(LotSize); // Bắt đầu khối giao dịch if(NewBar == true) { // Lệnh mua
if(FastMA > SlowMA && LastFastMA <= LastSlowMA &&
BuyMarketCount(Symbol(),MagicNumber) == 0) { 173 www.ZTCprep.com LẬP TRÌNH
TƯ VẤN CHUYÊN NGHIỆP // Đóng lệnh bán
if(SellMarketCount(Symbol(),MagicNumber) > 0) {
CloseAllSellOrders(Symbol(),MagicNumber,Slippage); } // Mở lệnh mua BuyTicket =
OpenBuyOrder(Symbol(),LotSize,UseSlippage,MagicNumber); // Sửa đổi đơn hàng
if(BuyTicket > 0 && (StopLoss > 0 || TakeProfit > 0)) {
OrderSelect(BuyTicket,SELECT_BY_TICKET); gấp đôi OpenPrice = OrderOpenPrice(); //
Tính toán và xác minh mức dừng lỗ và chốt lãi gấp đôi BuyStopLoss =
CalcBuyStopLoss(Symbol(),StopLoss,OpenPrice); if(BuyStopLoss > 0) BuyStopLoss =
Điều chỉnhBelowStopLevel(Symbol(), BuyStopLoss,5); gấp đôi BuyTakeProfit =
CalcBuyTakeProfit(Symbol(),TakeProfit, OpenPrice); if(BuyTakeProfit > 0)
BuyTakeProfit = Điều chỉnh trênStopLevel(Symbol(), BuyTakeProfit,5); // Thêm điểm
dừng lỗ và chốt lãi AddStopProfit(BuyTicket,BuyStopLoss,BuyTakeProfit); } } // Lệnh
bán if(FastMA < SlowMA && LastFastMA >= LastSlowMA &&
SellMarketCount(Symbol(),MagicNumber) == 0) {
if(BuyMarketCount(Symbol(),MagicNumber) > 0) { CloseAllBuyOrders(Symbol(),
MagicNumber,Trượt giá); } SellTicket =
OpenSellOrder(Symbol(),LotSize,UseSlippage,MagicNumber); if(SellTicket > 0 &&
(StopLoss > 0 || TakeProfit > 0)) { OrderSelect(SellTicket,SELECT_BY_TICKET);
OpenPrice = OrderOpenPrice(); gấp đôi SellStopLoss =
CalcSellStopLoss(Symbol(),StopLoss,OpenPrice); if(SellStopLoss > 0) SellStopLoss =
Điều chỉnhAboveStopLevel(Symbol(), SellStopLoss,5); gấp đôi SellTakeProfit =
CalcSellTakeProfit(Symbol(),TakeProfit, OpenPrice); 174 www.ZTCprep.com Phụ lục C
if(SellTakeProfit > 0) SellTakeProfit = Điều chỉnhBelowStopLevel(Symbol(),
SellTakeProfit,5); AddStopProfit(SellTicket,SellStopLoss,SellTakeProfit); } } } // Kết thúc
khối giao dịch // Điều chỉnh các điểm dừng
nếu(BuyMarketCount(Symbol(),MagicNumber) > 0 && TrailingStop > 0) {
BuyTrailingStop(Symbol(),TrailingStop,MinimumProfit,MagicNumber); }
if(SellMarketCount(Symbol(),MagicNumber) > 0 && TrailingStop > 0) {
SellTrailingStop(Symbol(),TrailingStop,MinimumProfit,MagicNumber); } trả về(0); }
Expert Advisor có chức năng – Lệnh chờ Đây là Expert Advisor có chức năng, sử dụng
lệnh dừng đang chờ xử lý: // Preprocessor #property Copyright "Andrew Young"
#include // Biến ngoài extern bool DynamicLotSize = true; bên ngoài gấp đôi
EquityPercent = 2; bên ngoài gấp đôi FixLotSize = 0,1; StopLoss kép bên ngoài = 50;
bên ngoài gấp đôi TakeProfit = 100; extern int TrailingStop = 50; extern int Lợi nhuận
tối thiểu = 50; extern int PendingPips = 1; extern int Trượt = 5; extern int MagicNumber
= 123; 175 www.ZTCprep.com CHUYÊN GIA CỐ VẤN LẬP TRÌNH extern int
FastMAPeriod = 10; extern int SlowMAPeriod = 20; bool bên ngoài CheckOncePerBar =
true; // Biến toàn cục int BuyTicket; int BánTicket; điểm sử dụng gấp đôi; int UseTrượt
trượt; ngày giờ CurrentTimeStamp; // Hàm khởi tạo int init() { UsePoint =
PipPoint(Symbol()); UseSlippage = GetSlippage(Symbol(),Slippage);
CurrentTimeStamp = Thời gian[0]; } // Start Function int start() { // Thực thi trên bar
open if(CheckOncePerBar == true) { int BarShift = 1; if(CurrentTimeStamp != Time[0]) {
CurrentTimeStamp = Time[0]; bool NewBar = đúng; } khác NewBar = false; } khác {
NewBar = true; BarShift = 0; } // Đường trung bình động gấp đôi FastMA =
iMA(NULL,0,FastMAPeriod,0,0,0,BarShift); gấp đôi SlowMA =
iMA(NULL,0,SlowMAPeriod,0,0,0,BarShift); 176 www.ZTCprep.com Phụ lục C // Tính
kích thước lô gấp đôi LotSize =
CalcLotSize(DynamicLotSize,EquityPercent,StopLoss,FixedLotSize); LotSize =
VerifyLotSize(LotSize); // Bắt đầu khối giao dịch if(NewBar == true) { // Lệnh mua
if(FastMA > SlowMA && BuyTicket == 0 && BuyMarketCount(Symbol(),MagicNumber)
== 0 && BuyStopCount(Symbol(),MagicNumber) == 0) { // Đóng lệnh bán
if(SellMarketCount(Symbol(),MagicNumber) > 0) {
CloseAllSellOrders(Symbol(),MagicNumber,Slippage); } // Xóa lệnh dừng bán
if(SellStopCount(Symbol(),MagicNumber) > 0) {
CloseAllSellStopOrders(Symbol(),MagicNumber); } BánTicket = 0; gấp đôi Giá đang
chờ xử lý = Cao[BarShift] + (PendingPips * UsePoint); PendingPrice = Điều chỉnh
trênStopLevel(Symbol(),PendingPrice,5); gấp đôi BuyStopLoss =
CalcBuyStopLoss(Symbol(),StopLoss,PendingPrice); if(BuyStopLoss > 0) BuyStopLoss
= Điều chỉnhBelowStopLevel(Symbol(),BuyStopLoss, 5,PendingPrice); gấp đôi
BuyTakeProfit = CalcBuyTakeProfit(Symbol(),TakeProfit,PendingPrice);
if(BuyTakeProfit > 0) BuyTakeProfit = Điều chỉnh trênStopLevel(Symbol(),
BuyTakeProfit,5,PendingPrice); BuyTicket =
OpenBuyStopOrder(Symbol(),LotSize,PendingPrice,BuyStopLoss,
BuyTakeProfit,UseSlippage,MagicNumber); } // Lệnh bán if(FastMA < SlowMA &&
SellTicket == 0 && SellMarketCount(Symbol(),MagicNumber) == 0 &&
SellStopCount(Symbol(),MagicNumber) == 0) {
if(BuyMarketCount(Symbol(),MagicNumber ) > 0) {
CloseAllBuyOrders(Symbol(),MagicNumber,Slippage); } 177 www.ZTCprep.com LẬP
TRÌNH CỐ VẤN CHUYÊN GIA if(BuyStopCount(Symbol(),MagicNumber) > 0) {
CloseAllBuyStopOrders(Symbol(),MagicNumber); } MuaVé = 0; Giá đang chờ xử lý =
Thấp[BarShift] - (PendingPips * UsePoint); PendingPrice = Điều
chỉnhBelowStopLevel(Symbol(),PendingPrice,5); gấp đôi SellStopLoss =
CalcSellStopLoss(Symbol(),StopLoss,PendingPrice); if(SellStopLoss > 0) SellStopLoss
= Điều chỉnhAboveStopLevel(Symbol(), SellStopLoss,5,PendingPrice); gấp đôi
SellTakeProfit = CalcSellTakeProfit(Symbol(),TakeProfit, PendingPrice);
if(SellTakeProfit > 0) Điều chỉnhBelowStopLevel(Symbol(),
SellTakeProfit,5,PendingPrice); SellTicket =
OpenSellStopOrder(Symbol(),LotSize,PendingPrice,SellStopLoss,
SellTakeProfit,UseSlippage,MagicNumber); } } // Kết thúc khối giao dịch // Điều chỉnh
các điểm dừng nếu(BuyMarketCount(Symbol(),MagicNumber) > 0 && TrailingStop > 0)
{ BuyTrailingStop(Symbol(),TrailingStop,MinimumProfit,MagicNumber); }
if(SellMarketCount(Symbol(),MagicNumber) > 0 && TrailingStop > 0) {
SellTrailingStop(Symbol(),TrailingStop,MinimumProfit,MagicNumber); } trả về(0); } 178
www.ZTCprep.com Phụ lục C 179 www.ZTCprep.com LẬP TRÌNH EXPERT Advisor
double CalcLotSize(bool argDynamicLotSize, double argEquityPercent,double
argStopLoss, double argFixedLotSize) { if(argDynamicLotSize == true && argStopLoss
> 0) { double RiskAmount = AccountEquity() * (argEquityPercent / 100); double
TickValue = MarketInfo(Symbol(),MODE_TICKVALUE); if(Điểm == 0,001 || Điểm ==
0,00001) TickValue *= 10; gấp đôi LotSize = (RiskAmount / argStopLoss) / TickValue; }
else LotSize = argFixedLotSize; return(LotSize); } double VerifyLotSize(double
argLotSize) { if(argLotSize < MarketInfo(Symbol(),MODE_MINLOT)) { argLotSize =
MarketInfo(Symbol(),MODE_MINLOT); } else if(argLotSize >
MarketInfo(Symbol(),MODE_MAXLOT)) { argLotSize =
MarketInfo(Symbol(),MODE_MAXLOT); } if(MarketInfo(Symbol(),MODE_LOTSTEP) ==
0,1) { argLotSize = NormalizeDouble(argLotSize,1); } else argLotSize =
NormalizeDouble(argLotSize,2); return(argLotSize); } 180 www.ZTCprep.com Phụ lục
D int OpenBuyOrder(string argSymbol, double argLotSize, double argSlippage, double
argMagicNumber, string argComment = "Buy Order") { while(IsTradeContextBusy())
Sleep(10); // Đặt lệnh mua int Ticket =
OrderSend(argSymbol,OP_BUY,argLotSize,MarketInfo(argSymbol,MODE_ASK),
argSlippage,0,0,argComment,argMagicNumber,0,Green); // Xử lý lỗi if(Ticket == -1) { int
ErrorCode = GetLastError(); chuỗi ErrDesc = ErrorDescription(ErrorCode); string
ErrAlert = StringConcatenate("Mở lệnh mua – Lỗi", ErrorCode,": ", ErrDesc); Cảnh
báo(ErrAlert); chuỗi ErrLog = StringConcatenate("Giá thầu:
",MarketInfo(argSymbol,MODE_BID), " Hỏi: ",MarketInfo(argSymbol,MODE_ASK)," Rất
nhiều: ",argLotSize); In(ErrLog); } vé khứ hồi); } int OpenSellOrder(string argSymbol,
double argLotSize, double argSlippage, double argMagicNumber, string argComment
= "Sell Order") { while(IsTradeContextBusy()) Sleep(10); // Đặt lệnh bán int Ticket =
OrderSend(argSymbol,OP_SELL,argLotSize,MarketInfo(argSymbol,MODE_BID),
argSlippage,0,0,argComment,argMagicNumber,0,Red); // Xử lý lỗi if(Ticket == -1) { int
ErrorCode = GetLastError(); chuỗi ErrDesc = ErrorDescription(ErrorCode); string
ErrAlert = StringConcatenate("Mở lệnh bán - Lỗi", ErrorCode, ": ",ErrDesc); Cảnh
báo(ErrAlert); chuỗi ErrLog = StringConcatenate("Giá thầu:
",MarketInfo(argSymbol,MODE_BID), " Hỏi: ",MarketInfo(argSymbol,MODE_ASK)," Rất
nhiều: ",argLotSize); In(ErrLog); } vé khứ hồi); } 181 www.ZTCprep.com LẬP TRÌNH CỐ
VẤN CHUYÊN GIA int OpenBuyStopOrder(string argSymbol, double argLotSize, double
argPendingPrice, double argStopLoss, double argTakeProfit, double argSlippage,
double argMagicNumber, datetime argExpiration = 0, string argComment = "Buy Stop
Order") { while (IsTradeContextBusy()) Ngủ(10); // Đặt lệnh dừng mua int Ticket =
OrderSend(argSymbol,OP_BUYSTOP,argLotSize,argPendingPrice,argSlippage,
argStopLoss,argTakeProfit,argComment,argMagicNumber,argExpiration,Green); // Xử
lý lỗi if(Ticket == -1) { int ErrorCode = GetLastError(); chuỗi ErrDesc =
ErrorDescription(ErrorCode); chuỗi ErrAlert = StringConcatenate("Mở lệnh dừng mua -
Lỗi", ErrorCode, ": ",ErrDesc); Cảnh báo(ErrAlert); chuỗi ErrLog =
StringConcatenate("Hỏi: ",MarketInfo(argSymbol,MODE_ASK), " Rất nhiều:
",argLotSize," Giá: ",argPendingPrice," Dừng: ",argStopLoss, " Lợi nhuận: ",argTakeProfit,"
Hết hạn: ", TimeToStr(argExpiration)); In(ErrLog); } vé khứ hồi); } int
OpenSellStopOrder(string argSymbol, double argLotSize, double argPendingPrice,
double argStopLoss, double argTakeProfit, double argSlippage, double
argMagicNumber, datetime argExpiration = 0, string argComment = "Sell Stop Order") {
while(IsTradeContextBusy()) Sleep(10) ; // Đặt lệnh dừng bán int Ticket =
OrderSend(argSymbol,OP_SELLSTOP,argLotSize,argPendingPrice,argSlippage,
argStopLoss,argTakeProfit,argComment,argMagicNumber,argExpiration,Red); // Xử lý
lỗi if(Ticket == -1) { int ErrorCode = GetLastError(); chuỗi ErrDesc =
ErrorDescription(ErrorCode); string ErrAlert = StringConcatenate("Mở lệnh dừng bán -
Lỗi", ErrorCode, ": ",ErrDesc); Cảnh báo(ErrAlert); chuỗi ErrLog =
StringConcatenate("Bid: ",MarketInfo(argSymbol,MODE_BID), " Lots: ",argLotSize,"
Price: ",argPendingPrice," Stop: ",argStopLoss, " Profit: ",argTakeProfit," Hết hạn: ",
TimeToStr(argExpiration)); 182 www.ZTCprep.com Phụ lục D In(ErrLog); } vé khứ hồi);
} int OpenBuyLimitOrder(string argSymbol, double argLotSize, double argPendingPrice,
double argStopLoss, double argTakeProfit, double argSlippage, double
argMagicNumber, datetime argExpiration, string argComment = "Lệnh giới hạn mua") {
while(IsTradeContextBusy()) Sleep(10); // Đặt lệnh giới hạn mua int Ticket =
OrderSend(argSymbol,OP_BUYLIMIT,argLotSize,argPendingPrice,argSlippage,
argStopLoss,argTakeProfit,argComment,argMagicNumber,argExpiration,Green); // Xử
lý lỗi if(Ticket == -1) { int ErrorCode = GetLastError(); chuỗi ErrDesc =
ErrorDescription(ErrorCode); string ErrAlert = StringConcatenate("Mở lệnh giới hạn
mua - Lỗi", ErrorCode, ": ",ErrDesc); Cảnh báo(ErrAlert); chuỗi ErrLog =
StringConcatenate("Bid: ",MarketInfo(argSymbol,MODE_BID), " Lots: ",argLotSize,"
Price: ",argPendingPrice," Stop: ",argStopLoss, " Profit: ",argTakeProfit," Hết hạn: ",
TimeToStr(argExpiration)); In(ErrLog); } vé khứ hồi); } int OpenSellLimitOrder(string
argSymbol, double argLotSize, double argPendingPrice, double argStopLoss, double
argTakeProfit, double argSlippage, double argMagicNumber, datetime argExpiration,
string argComment = "Sell Limit Order") { while(IsTradeContextBusy()) Sleep(10); //
Đặt lệnh giới hạn bán int Ticket =
OrderSend(argSymbol,OP_SELLLIMIT,argLotSize,argPendingPrice,argSlippage,
argStopLoss,argTakeProfit,argComment,argMagicNumber,argExpiration,Red); // Xử lý
lỗi if(Ticket == -1) { int ErrorCode = GetLastError(); chuỗi ErrDesc =
ErrorDescription(ErrorCode); 183 www.ZTCprep.com Chuỗi LẬP TRÌNH TƯ VẤN
CHUYÊN GIA ErrAlert = StringConcatenate("Mở lệnh dừng bán - Lỗi ",ErrorCode, ":
",ErrDesc); Cảnh báo(ErrAlert); chuỗi ErrLog = StringConcatenate("Hỏi:
",MarketInfo(argSymbol,MODE_ASK), " Rất nhiều: ",argLotSize," Giá: ",argPendingPrice,"
Dừng: ",argStopLoss, " Lợi nhuận: ",argTakeProfit," Hết hạn: ",
TimeToStr(argExpiration)); In(ErrLog); } vé khứ hồi); } double PipPoint(chuỗi Tiền tệ) {
int CalcDigits = MarketInfo(Tiền tệ,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 3)
gấp đôi CalcPoint = 0,01; khác nếu(CalcDigits == 4 || CalcDigits == 5) CalcPoint =
0,0001; return(CalcPoint); } int GetSlippage(string Tiền tệ, int SlippagePips) { int
CalcDigits = MarketInfo(Tiền tệ,MODE_DIGITS); if(CalcDigits == 2 || CalcDigits == 4)
double CalcSlippage = SlippagePips; else if(CalcDigits == 3 || CalcDigits == 5)
CalcSlippage = SlippagePips * 10; return(CalcSlippage); } bool CloseBuyOrder(string
argSymbol, int argCloseTicket, double argSlippage) {
OrderSelect(argCloseTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0) { double
CloseLots = OrderLots(); while(IsTradeContextBusy()) Ngủ(10); double ClosePrice =
MarketInfo(argSymbol,MODE_BID); bool Đã đóng =
OrderClose(argCloseTicket,CloseLots,ClosePrice,argSlippage,Green); if(Đóng == false)
{ int ErrorCode = GetLastError(); chuỗi ErrDesc = ErrorDescription(ErrorCode); string
ErrAlert = StringConcatenate("Đóng lệnh mua - Lỗi: ",ErrorCode, ": ",ErrDesc); Cảnh
báo(ErrAlert); 184 www.ZTCprep.com Phụ lục D chuỗi ErrLog =
StringConcatenate("Ticket: ",argCloseTicket," Bid: ",
MarketInfo(argSymbol,MODE_BID)); In(ErrLog); } } return(Đã đóng); } bool
CloseSellOrder(string argSymbol, int argCloseTicket, double argSlippage) {
OrderSelect(argCloseTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0) { double
CloseLots = OrderLots(); while(IsTradeContextBusy()) Ngủ(10); double ClosePrice =
MarketInfo(argSymbol,MODE_ASK); bool Đã đóng =
OrderClose(argCloseTicket,CloseLots,ClosePrice,argSlippage,Red); if(Đóng == false) {
int ErrorCode = GetLastError(); chuỗi ErrDesc = ErrorDescription(ErrorCode); string
ErrAlert = StringConcatenate("Đóng lệnh bán - Lỗi: ",ErrorCode, ": ",ErrDesc); Cảnh
báo(ErrAlert); chuỗi ErrLog = StringConcatenate("Ticket: ",argCloseTicket, " Ask:
",MarketInfo(argSymbol,MODE_ASK)); In(ErrLog); } } return(Đã đóng); } bool
ClosePendingOrder(string argSymbol, int argCloseTicket) {
OrderSelect(argCloseTicket,SELECT_BY_TICKET); if(OrderCloseTime() == 0) {
while(IsTradeContextBusy()) Ngủ(10); bool Đã xóa = OrderDelete(argCloseTicket,Red);
if(Đã xóa == false) { int ErrorCode = GetLastError(); 185 www.ZTCprep.com Chuỗi LẬP
TRÌNH TƯ VẤN CHUYÊN GIA ErrDesc = ErrorDescription(ErrorCode); string ErrAlert =
StringConcatenate("Đóng lệnh chờ - Lỗi: ", ErrorCode,": ",ErrDesc); Cảnh báo(ErrAlert);
chuỗi ErrLog = StringConcatenate("Ticket: ",argCloseTicket," Bid: ",
MarketInfo(argSymbol,MODE_BID)," Hỏi: ",MarketInfo(argSymbol,MODE_ASK));
In(ErrLog); } } return(Đã xóa); } double CalcBuyStopLoss(string argSymbol, int
argStopLoss, double argOpenPrice) { if(argStopLoss == 0) return(0); gấp đôi
BuyStopLoss = argOpenPrice - (argStopLoss * PipPoint(argSymbol));
return(BuyStopLoss); } double CalcSellStopLoss(string argSymbol, int argStopLoss,
double argOpenPrice) { if(argStopLoss == 0) return(0); gấp đôi SellStopLoss =
argOpenPrice + (argStopLoss * PipPoint(argSymbol)); return(SellStopLoss); } double
CalcBuyTakeProfit(string argSymbol, int argTakeProfit, double argOpenPrice) {
if(argTakeProfit == 0) return(0); gấp đôi BuyTakeProfit = argOpenprice + (argTakeProfit
* PipPoint(argSymbol)); return(BuyTakeProfit); } double CalcSellTakeProfit(string
argSymbol, int argTakeProfit, double argOpenPrice) { if(argTakeProfit == 0) return(0);
gấp đôi SellTakeProfit = argOpenPrice - (argTakeProfit * PipPoint(argSymbol));
return(SellTakeProfit); } 186 www.ZTCprep.com Phụ lục D bool
VerifyUpperStopLevel(string argSymbol, double argVerifyPrice, double argOpenPrice =
0) { double StopLevel = MarketInfo(argSymbol,MODE_STOPLEVEL) * Điểm;
if(argOpenPrice == 0) double OpenPrice = MarketInfo(argSymbol,MODE_ASK); khác
OpenPrice = argOpenprice; gấp đôi UpperStopLevel = Giá mở + StopLevel;
if(argVerifyPrice > UpperStopLevel) bool StopVerify = true; khác StopVerify = false;
return(StopVerify); } bool VerifyLowerStopLevel(string argSymbol, double
argVerifyPrice, double argOpenprice = 0) { double StopLevel =
MarketInfo(argSymbol,MODE_STOPLEVEL) * Điểm; if(argOpenPrice == 0) double
OpenPrice = MarketInfo(argSymbol,MODE_BID); khác OpenPrice = argOpenprice; gấp
đôi LowerStopLevel = Giá mở - StopLevel; if(argVerifyPrice < LowerStopLevel) bool
StopVerify = true; khác StopVerify = false; return(StopVerify); } double Điều chỉnh
trênStopLevel(string argSymbol, double argAdjustPrice, int argAddPips = 0, double
argOpenPrice = 0) { double StopLevel = MarketInfo(argSymbol,MODE_STOPLEVEL) *
Điểm; if(argOpenPrice == 0) double OpenPrice = MarketInfo(argSymbol,MODE_ASK);
khác OpenPrice = argOpenprice; gấp đôi UpperStopLevel = Giá mở + StopLevel;
if(argAdjustPrice <= UpperStopLevel) double Giá điều chỉnh = UpperStopLevel +
(argAddPips * PipPoint(argSymbol)); khác Giá điều chỉnh = argAdjustPrice; return(Giá
đã điều chỉnh); } 187 www.ZTCprep.com CHUYÊN GIA LẬP TRÌNH CỐ VẤN TƯ VẤN
double AdaptBelowStopLevel(string argSymbol, double argAdjustPrice, int argAddPips
= 0, double argOpenPrice = 0) { double StopLevel =
MarketInfo(argSymbol,MODE_STOPLEVEL) * Point; if(argOpenPrice == 0) double
OpenPrice = MarketInfo(argSymbol,MODE_BID); khác OpenPrice = argOpenprice; gấp
đôi LowerStopLevel = Giá mở - StopLevel; if(argAdjustPrice >= LowerStopLevel)
double Giá điều chỉnh = LowerStopLevel - (argAddPips * PipPoint(argSymbol)); khác
Giá điều chỉnh = argAdjustPrice; return(Giá đã điều chỉnh); } bool AddStopProfit(int
argTicket, double argStopLoss, double argTakeProfit) {
OrderSelect(argTicket,SELECT_BY_TICKET); gấp đôi OpenPrice = OrderOpenPrice();
while(IsTradeContextBusy()) Ngủ(10); // Sửa đổi đơn hàng bool TicketMod =
OrderModify(argTicket,OrderOpenPrice(),argStopLoss,argTakeProfit,0); // Xử lý lỗi
if(TicketMod == false) { int ErrorCode = GetLastError(); chuỗi ErrDesc =
ErrorDescription(ErrorCode); chuỗi ErrAlert = StringConcatenate("Thêm điểm dừng/lợi
nhuận - Lỗi", ErrorCode, ": ",ErrDesc); Cảnh báo(ErrAlert); chuỗi ErrLog =
StringConcatenate("Bid: ",MarketInfo(OrderSymbol(),MODE_BID), " Ask:
",MarketInfo(OrderSymbol(),MODE_ASK)" Ticket: ",argTicket," Stop: ", argStopLoss," Lợi
nhuận: ",argTakeProfit); In(ErrLog); } return(TicketMod); } 188 www.ZTCprep.com Phụ
lục D int TotalOrderCount(string argSymbol, int argMagicNumber) { int OrderCount;
for(int Bộ đếm = 0; Bộ đếm <= OrderTotal()-1; Bộ đếm++) {
OrderSelect(Counter,SELECT_BY_POS); if(OrderMagicNumber() == argMagicNumber
&& OrderSymbol() == argSymbol) { OrderCount++; } } return(Đếm đơn hàng); } int
BuyMarketCount(string argSymbol, int argMagicNumber) { int OrderCount; for(int Bộ
đếm = 0; Bộ đếm <= OrderTotal()-1; Bộ đếm++) {
OrderSelect(Counter,SELECT_BY_POS); if(OrderMagicNumber() == argMagicNumber
&& OrderSymbol() == argSymbol && OrderType() == OP_BUY) { OrderCount++; } }
return(Đếm đơn hàng); } int SellMarketCount(string argSymbol, int argMagicNumber) {
int OrderCount; for(int Bộ đếm = 0; Bộ đếm <= OrderTotal()-1; Bộ đếm++) {
OrderSelect(Counter,SELECT_BY_POS); if(OrderMagicNumber() == argMagicNumber
&& OrderSymbol() == argSymbol && OrderType() == OP_SELL) { OrderCount++; } }
return(Đếm đơn hàng); } 189 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA int
BuyStopCount(string argSymbol, int argMagicNumber) { int OrderCount; for(int Bộ
đếm = 0; Bộ đếm <= OrderTotal()-1; Bộ đếm++) {
OrderSelect(Counter,SELECT_BY_POS); if(OrderMagicNumber() == argMagicNumber
&& OrderSymbol() == argSymbol && OrderType() == OP_BUYSTOP) { OrderCount++; } }
return(Đếm đơn hàng); } int SellStopCount(string argSymbol, int argMagicNumber) {
int OrderCount; for(int Bộ đếm = 0; Bộ đếm <= OrderTotal()-1; Bộ đếm++) {
OrderSelect(Counter,SELECT_BY_POS); if(OrderMagicNumber() == argMagicNumber
&& OrderSymbol() == argSymbol && OrderType() == OP_SELLSTOP) { OrderCount++; } }
return(Đếm đơn hàng); } int BuyLimitCount(string argSymbol, int argMagicNumber) {
int OrderCount; for(int Bộ đếm = 0; Bộ đếm <= OrderTotal()-1; Bộ đếm++) {
OrderSelect(Counter,SELECT_BY_POS); if(OrderMagicNumber() == argMagicNumber
&& OrderSymbol() == argSymbol && OrderType() == OP_BUYLIMIT) { OrderCount++; } }
return(Đếm đơn hàng); } 190 www.ZTCprep.com Phụ lục D int SellLimitCount(string
argSymbol, int argMagicNumber) { int OrderCount; for(int Bộ đếm = 0; Bộ đếm <=
OrderTotal()-1; Bộ đếm++) { OrderSelect(Counter,SELECT_BY_POS);
if(OrderMagicNumber() == argMagicNumber && OrderSymbol() == argSymbol &&
OrderType() == OP_SELLLIMIT) { OrderCount++; } } return(Đếm đơn hàng); } void
CloseAllBuyOrders(string argSymbol, int argMagicNumber, int argSlippage) { for(int
Counter = 0; Counter <= OrderTotal()-1; Counter++) {
OrderSelect(Counter,SELECT_BY_POS); if(OrderMagicNumber() == argMagicNumber
&& OrderSymbol() == argSymbol && OrderType() == OP_BUY) { // Đóng đơn hàng int
CloseTicket = OrderTicket(); double CloseLots = OrderLots();
while(IsTradeContextBusy()) Ngủ(10); double ClosePrice =
MarketInfo(argSymbol,MODE_BID); bool Đã đóng =
OrderClose(CloseTicket,CloseLots,ClosePrice,argSlippage,Red); // Xử lý lỗi if(Closed
== false) { int ErrorCode = GetLastError(); chuỗi ErrDesc = ErrorDescription(ErrorCode);
string ErrAlert = StringConcatenate("Đóng tất cả đơn đặt hàng - Lỗi ", ErrorCode,":
",ErrDesc); Cảnh báo(ErrAlert); chuỗi ErrLog = StringConcatenate("Giá thầu: ",
MarketInfo(argSymbol,MODE_BID)," Vé: ",CloseTicket," Giá: ", ClosePrice); In(ErrLog); }
khác Bộ đếm--; } } } 191 www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA void
CloseAllSellOrders(string argSymbol, int argMagicNumber, int argSlippage) { for(int
Counter = 0; Counter <= OrderTotal()-1; Counter++) {
OrderSelect(Counter,SELECT_BY_POS) ; if(OrderMagicNumber() == argMagicNumber
&& OrderSymbol() == argSymbol && OrderType() == OP_SELL) { // Đóng đơn hàng int
CloseTicket = OrderTicket(); double CloseLots = OrderLots();
while(IsTradeContextBusy()) Ngủ(10); double ClosePrice =
MarketInfo(argSymbol,MODE_ASK); bool Đã đóng =
OrderClose(CloseTicket,CloseLots,ClosePrice,argSlippage,Red); // Xử lý lỗi if(Closed
== false) { int ErrorCode = GetLastError(); chuỗi ErrDesc = ErrorDescription(ErrorCode);
string ErrAlert = StringConcatenate("Đóng tất cả lệnh bán - Lỗi ", ErrorCode,":
",ErrDesc); Cảnh báo(ErrAlert); chuỗi ErrLog = StringConcatenate("Hỏi: ",
MarketInfo(argSymbol,MODE_ASK)," Vé: ",CloseTicket," Giá: ", ClosePrice); In(ErrLog); }
khác Bộ đếm--; } } } void CloseAllBuyStopOrders(string argSymbol, int
argMagicNumber) { for(int Counter = 0; Counter <= OrderTotal()-1; Counter++) {
OrderSelect(Counter,SELECT_BY_POS); if(OrderMagicNumber() == argMagicNumber
&& OrderSymbol() == argSymbol && OrderType() == OP_BUYSTOP) { // Xóa đơn hàng
int CloseTicket = OrderTicket(); 192 www.ZTCprep.com Phụ lục D
while(IsTradeContextBusy()) Sleep(10); bool Đã đóng = OrderDelete(CloseTicket,Red);
// Xử lý lỗi if(Closed == false) { int ErrorCode = GetLastError(); chuỗi ErrDesc =
ErrorDescription(ErrorCode); string ErrAlert = StringConcatenate("Đóng tất cả các lệnh
dừng mua - ", "Error",ErrorCode,": ",ErrDesc); Cảnh báo(ErrAlert); chuỗi ErrLog =
StringConcatenate("Bid: ", MarketInfo(argSymbol,MODE_BID)," Ask: ",
MarketInfo(argSymbol,MODE_ASK)," Ticket: ",CloseTicket); In(ErrLog); } khác Bộ đếm--;
} } } void CloseAllSellStopOrders(string argSymbol, int argMagicNumber) { for(int
Counter = 0; Counter <= OrderTotal()-1; Counter++) {
OrderSelect(Counter,SELECT_BY_POS); if(OrderMagicNumber() == argMagicNumber
&& OrderSymbol() == argSymbol && OrderType() == OP_SELLSTOP) { // Xóa đơn hàng
int CloseTicket = OrderTicket(); while(IsTradeContextBusy()) Ngủ(10); bool Đã đóng =
OrderDelete(CloseTicket,Red); // Xử lý lỗi if(Closed == false) { int ErrorCode =
GetLastError(); chuỗi ErrDesc = ErrorDescription(ErrorCode); string ErrAlert =
StringConcatenate("Đóng tất cả các lệnh dừng bán - ", "Error", ErrorCode,": ",ErrDesc);
Cảnh báo(ErrAlert); 193 www.ZTCprep.com Chuỗi LẬP TRÌNH TƯ VẤN CHUYÊN
NGHIỆP ErrLog = StringConcatenate("Bid: ", MarketInfo(argSymbol,MODE_BID)," Ask: ",
MarketInfo(argSymbol,MODE_ASK)," Ticket: ",CloseTicket); In(ErrLog); } khác Bộ đếm--;
} } } void CloseAllBuyLimitOrders(string argSymbol, int argMagicNumber) { for(int
Counter = 0; Counter <= OrderTotal()-1; Counter++) {
OrderSelect(Counter,SELECT_BY_POS); if(OrderMagicNumber() == argMagicNumber
&& OrderSymbol() == argSymbol && OrderType() == OP_BUYLIMIT) { // Xóa đơn hàng
int CloseTicket = OrderTicket(); while(IsTradeContextBusy()) Ngủ(10); bool Đã đóng =
OrderDelete(CloseTicket,Red); // Xử lý lỗi if(Closed == false) { int ErrorCode =
GetLastError(); chuỗi ErrDesc = ErrorDescription(ErrorCode); string ErrAlert =
StringConcatenate("Đóng tất cả các lệnh giới hạn mua - ","Error ",ErrorCode,":
",ErrDesc); Cảnh báo(ErrAlert); chuỗi ErrLog = StringConcatenate("Bid: ",
MarketInfo(argSymbol,MODE_BID)," Ask: ", MarketInfo(argSymbol,MODE_ASK)," Ticket:
",CloseTicket); In(ErrLog); } khác Bộ đếm--; } } } 194 www.ZTCprep.com Phụ lục D void
CloseAllSellLimitOrders(string argSymbol, int argMagicNumber) { for(int Counter = 0;
Counter <= OrderTotal()-1; Counter++) { OrderSelect(Counter,SELECT_BY_POS);
if(OrderMagicNumber() == argMagicNumber && OrderSymbol() == argSymbol &&
OrderType() == OP_SELLLIMIT) { // Xóa đơn hàng int CloseTicket = OrderTicket();
while(IsTradeContextBusy()) Ngủ(10); bool Đã đóng = OrderDelete(CloseTicket,Red); //
Xử lý lỗi if(Closed == false) { int ErrorCode = GetLastError(); chuỗi ErrDesc =
ErrorDescription(ErrorCode); string ErrAlert = StringConcatenate("Đóng tất cả các lệnh
giới hạn bán - ", "Error", ErrorCode,": ",ErrDesc); Cảnh báo(ErrAlert); chuỗi ErrLog =
StringConcatenate("Bid: ", MarketInfo(argSymbol,MODE_BID)," Ask: ",
MarketInfo(argSymbol,MODE_ASK)," Ticket: ",CloseTicket); In(ErrLog); } khác Bộ đếm--;
} } } void BuyTrailingStop(string argSymbol, int argTrailingStop, int argMinProfit, int
argMagicNumber) { for(int Counter = 0; Counter <= OrderTotal()-1; Counter++) {
OrderSelect(Counter,SELECT_BY_POS); // Tính mức dừng tối đa và lợi nhuận tối thiểu
gấp đôi MaxStopLoss = MarketInfo(argSymbol,MODE_BID) - (argTrailingStop *
PipPoint(argSymbol)); MaxStopLoss = NormalizeDouble(MaxStopLoss,
MarketInfo(OrderSymbol(),MODE_DIGITS)); double CurrentStop =
NormalizeDouble(OrderStopLoss(), MarketInfo(OrderSymbol(),MODE_DIGITS)); 195
www.ZTCprep.com CHUYÊN GIA CỐ VẤN LẬP TRÌNH double PipsProfit =
MarketInfo(argSymbol,MODE_BID) - OrderOpenPrice(); nhân đôi MinProfit =
argMinProfit * PipPoint(argSymbol); // Sửa đổi Dừng if(OrderMagicNumber() ==
argMagicNumber && OrderSymbol() == argSymbol && OrderType() == OP_BUY &&
CurrentStop < MaxStopLoss && PipsProfit >= MinProfit) { bool Trailed =
OrderModify(OrderTicket(),OrderOpenPrice(), MaxStopLoss, OrderTakeProfit(),0); // Xử
lý lỗi if(Trailed == false) { int ErrorCode = GetLastError(); chuỗi ErrDesc =
ErrorDescription(ErrorCode); chuỗi ErrAlert = StringConcatenate("Buy Trailing Stop –
Error ", ",ErrorCode,": ",ErrDesc); Alert(ErrAlert); string ErrLog = StringConcatenate("Bid:
", MarketInfo(argSymbol,MODE_BID)" Ticket: " ,OrderTicket()," Stop: ", OrderStopLoss(),"
Trail: ",MaxStopLoss); Print(ErrLog); } } } } void SellTrailingStop(string argSymbol, int
argTrailingStop, int argMinProfit, int argMagicNumber) { for(int Bộ đếm = 0; Bộ đếm <=
OrderTotal()-1; Counter++) { OrderSelect(Counter,SELECT_BY_POS); // Tính mức dừng
tối đa và lợi nhuận tối thiểu gấp đôi MaxStopLoss =
MarketInfo(argSymbol,MODE_ASK) + (argTrailingStop * PipPoint(argSymbol));
MaxStopLoss = NormalizeDouble(MaxStopLoss,
MarketInfo(OrderSymbol(),MODE_DIGITS)); double CurrentStop =
NormalizeDouble(OrderStopLoss(), MarketInfo(OrderSymbol(),MODE_DIGITS)); double
PipsProfit = OrderOpenprice() - MarketInfo(argSymbol,MODE_ASK); gấp đôi MinProfit
= argMinProfit * PipPoint(argSymbol); 196 www.ZTCprep.com Phụ lục D // Sửa đổi
Dừng if(OrderMagicNumber() == argMagicNumber && OrderSymbol() == argSymbol
&& OrderType() == OP_SELL && (CurrentStop > MaxStopLoss || CurrentStop == 0) &&
PipsProfit >= MinProfit) { bool Trailed =
OrderModify(OrderTicket(),OrderOpenPrice(),MaxStopLoss, OrderTakeProfit(),0); // Xử
lý lỗi if(Trailed == false) { int ErrorCode = GetLastError(); chuỗi ErrDesc =
ErrorDescription(ErrorCode); chuỗi ErrAlert = StringConcatenate("Sell Trailing Stop -
Error ", ErrorCode,": ",ErrDesc); Cảnh báo(ErrAlert); chuỗi ErrLog =
StringConcatenate("Hỏi: ", MarketInfo(argSymbol,MODE_ASK)," Ticket: ",OrderTicket(),"
Stop: ", OrderStopLoss()," Trail: ",MaxStopLoss); In(ErrLog); } } } } 197
www.ZTCprep.com CHƯƠNG TRÌNH CỐ VẤN CHUYÊN NGHIỆP Phụ lục E Chỉ báo tùy
chỉnh Đây là mã cho chỉ báo tùy chỉnh từ chương 9: #property Copyright "Andrew
Young" #property Indicator_chart_window #property Indicator_buffers 3 #property
Indicator_color1 DeepSkyBlue #property Indicator_color2 DeepSkyBlue #property
Indicator_color3 DeepSkyBlue // Biến ngoài extern int BandsPeriod = 20; extern int
BandsShift = 0; extern int BandsMethod = 1; extern int BandsPrice = 0; extern int Độ
lệch = 1; // Bộ đệm đôi EMA[]; gấp đôi UpperBand[]; gấp đôi LowerBand[]; // Init int
init() { SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,EMA); SetIndexLabel(0,"EMA");
SetIndexStyle(1,DRAW_LINE); SetIndexBuffer(1,UpperBand);
SetIndexLabel(1,"UpperBand"); SetIndexStyle(2,DRAW_LINE);
SetIndexBuffer(2,LowerBand); SetIndexLabel(2,"LowerBand"); trở lại (0); } 198
www.ZTCprep.com Phụ lục E // Bắt đầu int start() { int count_bars =
IndicatorCounted(); int CalculateBars = Thanh - count_bars; for(int Count =
CalculateBars; Count >= 0; Count--) { EMA[Count] =
iMA(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice,Count); đôi StdDev =
iStdDev(NULL,0,BandsPeriod,BandsShift,BandsMethod,BandsPrice, Count);
UpperBand[Count] = EMA[Count] + (StdDev * Độ lệch); LowerBand[Count] =
EMA[Count] - (StdDev * Độ lệch); } trả về(0); } 199 www.ZTCprep.com
www.ZTCprep.com Chỉ mục A DayOfWeek() ................................. ............. 114 Gỡ lỗi
................................................. ....... 141 AccountBroker() .................... 125 toán tử mặc
định . .................... 130 AccountEquity() ........... ............ 50, 132 Giá trị mặc định
............. ............ 12 Tên tài khoản() .................... ............ 125 deinit() ........... ............ 17
Số tài khoản() .................... .. 125 DLL .................................................... ........... 75 Cảnh
báo() .................... ............ 54 kiểu dữ liệu kép .................... .......... 9 Hằng số giá áp
dụng........... 102 Mảng ...... ................................... 134 E Hỏi
................................................................. ............ 20 ECN/STP .................... ............ 20, 42
B toán tử khác ............ ..... 104 EMPTY_VALUE .................................... 102
Thanh................................................................................. ...... 150 Mã lỗi
.................................... .... 143 Giá thầu .................................... ............ 20 Xử lý lỗi
................................................. ........... 53 kiểu dữ liệu boolean .................................... ... 9
ErrorDescription() .................... 54 Toán tử Boolean ........ ...................... 105 Ký tự
thoát........... ............ 122 Điểm hòa vốn ................................... ............ 90 Expert Advisor
Wizard .. 14 toán tử ngắt .. ................................... 130 Hết hạn ..........
................................... 21 Bộ đệm ........... ................................. 146 biến ngoài ....
.................... 16 Toán tử trường hợp C F ........... ................... 130 FIFO ....................
...................................... 85 Đóng[] ....... .................................... 94 Định dạng tập tin ...
................................... Kiểu dữ liệu 4 màu ................................. 9 Vị trí tập tin ..........
...................................... 5 Bình luận() .... ................................... 122 dành cho người vận
hành....... ................................... 80 Bình luận ........ ...................................... 8 Đối số của
hàm ....... .................... 12 Lỗi biên dịch .................... ................... 144 Chức năng
.................... .................... 10 Toán tử ghép .................... ............ 8 hằng
số.................................. ............ 10 G Chỉ báo tùy chỉnh ................................. .98
GetLastError() .................................... 54 D Biến toàn cục .. ...................... 136 Biến
phạm vi toàn cầu ........... ............ 13, 17 Kiểu dữ liệu ................................. ................... 9
GlobalVariableDel() .................... .137 Cửa sổ dữ liệu .................................... 100
GlobalVariableDeleteAll( ) .... 137 Hằng số ngày giờ .................... ...... 112
GlobalVariableGet() ................... 137 kiểu dữ liệu ngày giờ ........ ...................... 9
GlobalVariableSet() .................... ............ 136 biến ngày giờ .................... 112 Ngày()
................................................................. ... 114 www.ZTCprep.com H Thấp[]
...................... ...................... 94 Cao[]............ ...................... 94 M giờ() ....................
...................... 114 Con số kỳ diệu ............. .................... 23 I Lệnh thị trường ....................
...................... 20 Thông tin thị trường() .................... ...................... 29 iClose()
.................... ................... 94 Martingale .................... ...................... 138 iCustom()
............ ...................... 98 MathPow() .................... ...................... 141 Mã định danh
............. .................... 8 Kích thước lô tối đa .................... ...................... 51 nếu toán
tử........... ...................... 103 MessageBox() .................... ...... 125 iHigh() ....................
...................... 94 MetaEditor .................... ...................... 6 iHighest() ....................
...................... 32 Kích thước lô tối thiểu ........... ............ 51 iLow() ....................
...................... 94 Phút() .................... ...................... 114 iLowest() ......................
...................... 31 Thông số chế độ .................... ...................... 98 iMA() ....................
................... 96 Tháng() .......... .................... 114 Bao gồm các tập tin.....