Download as pdf or txt
Download as pdf or txt
You are on page 1of 166

www.ZTCprep.com www.ZTCprep.

com Chuyên gia tư vấn Lập trình Tạo hệ thống giao


dịch tự động trong MQL cho MetaTrader 4 Andrew R. Young Edgehill Publishing
www.ZTCprep.com IN THỨ HAI Bản quyền © 2010, Andrew R. Young. Đã đăng ký Bản
quyền. Được xuất bản bởi Nhà xuất bản Edgehill, Nashville, TN. Tuyên bố miễn trừ
trách nhiệm bảo hành: Mặc dù chúng tôi cố gắng đảm bảo rằng tài liệu trong cuốn
sách này là chính xác nhưng nhà xuất bản không chịu trách nhiệm về tính chính xác
hoặc tính đầy đủ của cuốn sách này và đặc biệt từ chối mọi bảo đảm ngụ ý về khả
năng bán được hoặc sự phù hợp cho một mục đích cụ thể. Cả tác giả và nhà xuất bản
đều không chịu trách nhiệm về bất kỳ tổn thất lợi nhuận hoặc bất kỳ thiệt hại phi
thương mại hoặc thương mại nào khác, bao gồm nhưng không giới hạn ở các thiệt hại
do hậu quả, ngẫu nhiên, đặc biệt hoặc các thiệt hại khác. "MetaTrader 4," "MQL" và
"chuyên gia cố vấn" là thương hiệu của MetaQuotes Software Corp. Cuốn sách này và
nhà xuất bản của nó không được xác nhận hoặc liên kết với MetaQuotes Software
Corp dưới bất kỳ hình thức nào. Để biết thêm thông tin về cuốn sách này, bao gồm các
cập nhật, tin tức và các ấn bản mới, vui lòng truy cập trang web của chúng tôi tại
http://www.expertadvisorbook.com/. ISBN: 978-0-9826459-0-1 www.ZTCprep.com
Mục lục Giới thiệu 1 Giới thiệu về cuốn sách này 2 Lưu ý về MQL 5 2 Các quy ước được
sử dụng trong cuốn sách này 3 Giới thiệu về MQL 4 Giới thiệu về MetaEditor 4 Các khái
niệm cơ bản 7 Bố cục của Tệp MQ4 14 Đặt lệnh 20 Đặt lệnh, Hỏi & chênh lệch 20 Loại
lệnh 20 Quy trình đặt lệnh 21 OrderSend() 22 Tính toán mức dừng lỗ và chốt lãi 25
Truy xuất thông tin lệnh 32 Đóng lệnh 34 Chuyên gia cố vấn đơn giản 36 Đặt lệnh nâng
cao 42 Sửa đổi 42 Xác minh điểm dừng và giá đặt hàng đang chờ xử lý 45 Tính toán
kích thước lô 49 Những cân nhắc khác 52 Kết hợp tất cả lại với nhau 57 Làm việc với
các hàm 64 Thêm điểm dừng lỗ và chốt lời 73 Sử dụng tệp bao gồm 74 Sử dụng thư
viện 74 Một chuyên gia cố vấn đơn giản (có chức năng) 75 www. ZTCprep.com Quản
lý lệnh 80 Vòng lặp lệnh 80 Đếm lệnh 82 Điểm dừng treo 87 Cập nhật Expert Advisor
92 Điều kiện lệnh và chỉ báo 94 Dữ liệu giá 94 Chỉ báo 95 Hằng số chỉ báo 102 Đánh
giá điều kiện giao dịch 103 So sánh các giá trị chỉ báo giữa các thanh 108 Làm việc
với thời gian và ngày 112 Biến ngày giờ 112 Hàm ngày và giờ 114 Tạo bộ đếm thời
gian đơn giản 115 Thực thi trên thanh mở 117 Mẹo và thủ thuật 122 Thoát ký tự 122
Sử dụng nhận xét biểu đồ 122 Kiểm tra cài đặt 123 Giới hạn bản demo hoặc tài khoản
124 MessageBox() 125 Cảnh báo qua email 127 Thử lại khi có lỗi 128 Sử dụng đơn
đặt hàng Nhận xét Là mã định danh 131 Kiểm tra ký quỹ 132 Kiểm tra chênh lệch 132
Nhiều đơn đặt hàng 133 Biến toàn cầu 136 Kiểm tra lợi nhuận đơn hàng 137
Martingale 138 Gỡ lỗi Expert Advisor của bạn 141 www.ZTCprep.com Chỉ báo và tập
lệnh tùy chỉnh 146 Bộ đệm 146 Tạo chỉ báo tùy chỉnh 146 Tập lệnh 152 Phụ lục A 154
Expert Advisor đơn giản 154 Expert Advisor đơn giản với các lệnh chờ 156 Phụ lục B
160 Expert Advisor nâng cao 160 Expert Advisor nâng cao với các lệnh chờ 166 Phụ
lục C 172 Expert Advisor với các chức năng 172 Expert Advisor với các chức năng –
Lệnh chờ 175 Phụ lục D 180 Bao gồm tệp 180 Phụ lục E 198 Chỉ báo Tùy chỉnh 198
www.ZTCprep.com www.ZTCprep.com Giới thiệu Giới thiệu Thị trường ngoại hối đã
nhanh chóng trở thành một trong những thị trường giao dịch phổ biến nhất trong
những năm gần đây. Vì hoạt động suốt ngày đêm, đòn bẩy cao và yêu cầu ký quỹ thấp,
hàng nghìn người bình thường đã trở thành nhà giao dịch tích cực. MetaTrader 4
(thường được viết tắt là MT4) đã trở thành một trong những nền tảng giao dịch ngoại
hối phổ biến nhất. Được phát triển bởi Tập đoàn phần mềm MetaQuotes, MetaTrader
được cung cấp bởi hàng trăm nhà môi giới ngoại hối trên toàn thế giới, bao gồm
những tên tuổi lớn như GAIN Capital, FXCM, Alpari và Interbank FX. Sự phổ biến của
MetaTrader bắt nguồn từ thực tế là nó miễn phí, được nhà môi giới hỗ trợ và bao gồm
nhiều công cụ phân tích kỹ thuật hữu ích. Nhưng có lẽ lý do lớn nhất dẫn đến thành
công của MetaTrader chính là ngôn ngữ lập trình MQL mạnh mẽ. MQL đã giúp các nhà
giao dịch có thể lập trình các chỉ báo tùy chỉnh của riêng họ và chiến lược giao dịch tự
động mà không phải trả một xu nào cho phần mềm. Các gói giao dịch tương tự cho cổ
phiếu và hợp đồng tương lai có thể có giá trên 1000 USD. Một cộng đồng các nhà giao
dịch và lập trình viên trên toàn thế giới đã phát triển, cung cấp hàng trăm cố vấn và chỉ
báo chuyên gia thương mại và miễn phí, cũng như các dịch vụ và lời khuyên về lập
trình. Sự giống nhau của MQL với các ngôn ngữ như C giúp các lập trình viên có kinh
nghiệm tiếp thu tương đối dễ dàng và bản thân ngôn ngữ này đã được ghi chép đầy
đủ. Nhưng học cách lập trình chiến lược giao dịch hiệu quả trong MQL là một quá trình
thử và sai. MQL là ngôn ngữ cấp độ tương đối thấp và do đó, người lập trình cần tạo
các quy trình tùy chỉnh để xử lý nhiều chức năng giao dịch phổ biến. Ví dụ, việc mã hóa
một thứ gì đó đơn giản như dấu dừng cuối có thể gây khó khăn cho người lập trình
MQL mới. Có nhiều yếu tố phải được xem xét khi lập trình một chiến lược giao dịch tự
động mạnh mẽ và bản thân MetaTrader có nhiều đặc điểm riêng mà lập trình viên cần
lưu ý. Có thể mất hàng chục giờ khắc phục sự cố và thực hành để tìm hiểu các kỹ
thuật cần thiết để lập trình cố vấn chuyên gia. Cuốn sách này hy vọng sẽ rút ngắn thời
gian học tập cho các lập trình viên cố vấn chuyên gia mới. Ở đây tôi sẽ trình bày nhiều
mẹo và thủ thuật mà tôi đã học được trong hàng trăm giờ làm việc với các chuyên gia
cố vấn về mã hóa trong vài năm qua. 1 www.ZTCprep.com CHƯƠNG TRÌNH CỐ VẤN
CHUYÊN NGHIỆP Giới thiệu về cuốn sách này Khi bạn đọc xong cuốn sách này, bạn
nên có kiến ​thức cần thiết để tạo ra các chiến lược giao dịch tự động mạnh mẽ của
riêng mình trong MQL, bao gồm các tính năng giao dịch phổ biến như điểm dừng treo,
quản lý tiền và nhiều tính năng khác. hơn. Bạn cũng sẽ học cách xây dựng một chỉ báo
đơn giản bằng cách sử dụng các hàm chỉ báo tích hợp sẵn. Cuốn sách này giả định
rằng người đọc có kiến ​thức về giao dịch ngoại hối và phân tích kỹ thuật nói chung.
Người đọc phải thành thạo trong việc sử dụng các cố vấn và chỉ báo chuyên gia trong
MetaTrader. Mặc dù không có kiến ​thức lập trình trước đó, nhưng người đọc sẽ được
hưởng lợi từ việc có một số kỹ năng lập trình cơ bản và làm quen với các khái niệm
như biến, cấu trúc điều khiển, hàm và cú pháp ngôn ngữ lập trình hiện đại. Chúng ta sẽ
đi sâu vào các giải pháp mã hóa cho các vấn đề cụ thể. Mọi nỗ lực được thực hiện để
giải thích các khái niệm mới khi chúng được giới thiệu, tuy nhiên cuốn sách này không
nhằm mục đích tham khảo ngôn ngữ. Tham chiếu MQL tại http://docs.mql4.com thực
hiện công việc đó một cách xuất sắc. Tham chiếu MQL cũng được tích hợp vào
MetaEditor IDE đi kèm với MetaTrader. Mặc dù chúng tôi sẽ cố gắng đề cập đến mọi
thứ cần thiết và liên quan đến việc phát triển cố vấn chuyên gia, nhưng chúng tôi sẽ
không thể đề cập đến mọi yếu tố của ngôn ngữ MQL. Có nhiều chức năng chuyên biệt
trong MQL thường không được sử dụng trong chương trình cố vấn chuyên gia. Đặc
biệt, chúng ta sẽ không thảo luận về các hàm mảng, thao tác với tệp, đối tượng, cửa sổ
và hầu hết các hàm chuỗi hoặc hàm chuyển đổi. Trang web MQL4 chính thức tại
http://www.mql4.com có ​một cuốn sách miễn phí về lập trình MQL có thể đóng vai trò
là nguồn tài nguyên hữu ích và bổ sung. Có nhiều bài viết giàu thông tin đề cập đến
các khái niệm lập trình cơ bản và nâng cao trong MQL, một thư viện mã với các chỉ
báo và ví dụ bổ sung cũng như một diễn đàn nơi bạn có thể yêu cầu trợ giúp về các
câu hỏi lập trình của mình. Các ví dụ về mã và kỹ thuật tôi dạy trong cuốn sách này đã
mang lại hiệu quả cho tôi. Tôi cố gắng giữ mọi thứ đơn giản nhất có thể mà không làm
mất đi chức năng. Điều đó có nghĩa là luôn có nhiều cách để hoàn thành một việc gì
đó và điều này đặc biệt đúng trong lập trình. Có những phương pháp hợp lý như nhau
để đạt được kết quả tương tự và bạn có thể khám phá ra cách tốt hơn để làm điều gì
đó. Nhiều ví dụ về mã nguồn trong cuốn sách này cũng như các phụ lục đầy đủ có sẵn
để tải xuống tại trang web chính thức của cuốn sách,
http://www.expertadvisorbook.com/. Bằng cách này, bạn có thể tiết kiệm thời gian tự
mình gõ tất cả các ví dụ. Hãy thoải mái sửa đổi mã nguồn cho nhu cầu của riêng bạn.
2 www.ZTCprep.com Giới thiệu Lưu ý về MQL 5 Theo văn bản này, phiên bản tiếp theo
của nền tảng MetaTrader đang trong giai đoạn thử nghiệm beta mở. Sẽ có một số
thay đổi đáng kể đối với phiên bản MQL mới nhất. MetaQuotes đã báo cáo rằng
MetaTrader 5 sẽ không tương thích ngược với các chương trình MetaTrader 4. Do đó,
bất kỳ chương trình nào được viết bằng MQL 4 sẽ cần phải được viết lại hoặc cập nhật
cho MQL 5. Cuốn sách này đề cập đến MetaTrader 4, vì đây là phiên bản tôi đã lập
trình trong vài năm qua và hiện là phiên bản đang được sử dụng bởi các nhà môi giới
ngoại hối. Kể từ khi MetaTrader 4 được phát hành vào năm 2005, giao dịch Forex đã
trở nên phổ biến. MetaTrader đã trở thành nền tảng giao dịch ngoại hối phổ biến nhất
và đã có hàng nghìn chiến lược giao dịch và chỉ báo được viết bằng MQL 4. Tôi dự
đoán quá trình chuyển đổi sang MetaTrader 5 sẽ diễn ra dần dần. Các nhà môi giới sẽ
tiếp tục hỗ trợ MetaTrader 4 trong một thời gian, vì vậy các chương trình bạn viết trong
MQL 4 sẽ không bị lỗi thời ngay lập tức. Các khái niệm trong cuốn sách này sẽ không
thay đổi mặc dù một số hàm và cú pháp sẽ thay đổi. Thử thách sẽ là tìm hiểu các tính
năng mới của MQL 5 và kết hợp nó vào mã hiện có của bạn. Ấn bản thứ hai của cuốn
sách này sẽ được phát hành sau bản phát hành cuối cùng của MetaTrader 5. Đối với
những người đã mua cuốn sách này, mã nguồn cập nhật và hướng dẫn MQL4 đến
MQL5 sẽ có sẵn trên trang web của chúng tôi, http://www.expertadvisorbook .com/.
Các quy ước được sử dụng trong cuốn sách này Các thành phần ngôn ngữ MQL, ví dụ
mã nguồn, vị trí tệp và URL sẽ được hiển thị bằng phông chữ có chiều rộng cố định.
Phông chữ đậm lớn hơn sẽ được sử dụng cho văn bản nội tuyến. Các khối mã nguồn
sẽ được thụt vào. Bất kỳ văn bản in đậm nào xuất hiện trong khối mã nguồn thụt lề
đều cho biết mã đã được cập nhật hoặc thay đổi so với ví dụ trước. Khối mã nguồn Mã
nguồn được cập nhật Các từ in nghiêng cho biết một khái niệm mới đang được giới
thiệu hoặc định nghĩa. Các tham chiếu đến các phần và chủ đề trong MQL Reference
sẽ được hiển thị in nghiêng. Các tham chiếu đến các thành phần của giao diện
MetaTrader 4, bao gồm cửa sổ, hộp thoại, nút hoặc mục menu, cũng sẽ được hiển thị
bằng chữ in nghiêng. 3 www.ZTCprep.com LẬP TRÌNH Expert Advisor Chương 1 Giới
thiệu về MQL Giới thiệu về MetaEditor Expert Advisor là gì? Expert Advisor là một
chương trình giao dịch tự động được viết bằng MQL. Expert Advisor (thường được viết
tắt là EA) có thể đặt, sửa đổi và đóng lệnh theo thuật toán của hệ thống giao dịch. EA
thường sử dụng các chỉ báo để tạo tín hiệu giao dịch. Các chỉ báo này có thể là những
chỉ báo đi kèm với MetaTrader hoặc chúng có thể là các chỉ báo tùy chỉnh. Chỉ báo là
một công cụ phân tích kỹ thuật tính toán dữ liệu giá để đưa ra giải thích về hoạt động
thị trường. Một chỉ báo vẽ các đường hoặc đối tượng trên biểu đồ. Các chỉ báo không
thể đặt, sửa đổi hoặc đóng lệnh. Ví dụ về các chỉ báo bao gồm đường trung bình động
và ngẫu nhiên. Tập lệnh là một cố vấn chuyên gia được đơn giản hóa, thực hiện một
nhiệm vụ duy nhất, chẳng hạn như đặt lệnh chờ xử lý hoặc đóng tất cả các lệnh trên
biểu đồ. Một số tập lệnh hữu ích được bao gồm trong MetaTrader. Định dạng tệp Các
tệp có phần mở rộng .mq4 là các tệp mã nguồn. Đây là những tệp chúng tôi chỉnh sửa
trong MetaEditor. Khi tệp .mq4 được biên dịch, tệp .ex4 sẽ được tạo. Các tệp có phần
mở rộng .ex4 là các tệp thực thi. Đây là những tệp chúng tôi chạy trong MetaTrader.
Những tập tin này không thể mở được trong MetaEditor. Nếu bạn chỉ có tệp .ex4 cho
EA hoặc chỉ báo, biểu tượng bên cạnh tên tệp trong cửa sổ Điều hướng của
MetaTrader sẽ có màu xám. Các tệp có phần mở rộng .mqh là các tệp bao gồm. Các
tệp này chứa các hàm do người dùng tạo được tham chiếu trong tệp .mq4. Trong quá
trình biên dịch, trình biên dịch "bao gồm" nội dung của tệp .mqh trong tệp .ex4. Chúng
ta sẽ tìm hiểu thêm về việc bao gồm các tập tin sau. Phần mở rộng .mqt được sử dụng
cho các tệp mẫu. Mặc dù những tệp này có thể được mở trong MetaTrader nhưng loại
tệp này không được liên kết với chương trình trong Windows. Các mẫu được sử dụng
để tạo tệp mới bằng Expert Advisor Wizard trong MetaEditor. 4 www.ZTCprep.com
Giới thiệu về MQL Bạn có thể tạo các mẫu của riêng mình nếu muốn, nhưng chúng tôi
sẽ không đề cập đến việc tạo mẫu trong cuốn sách này. Tài liệu MetaTrader sẽ cho
bạn biết tất cả những gì bạn cần biết về cách tạo mẫu. Các chỉ báo, chuyên gia cố vấn,
thư viện và tập lệnh đều có chung phần mở rộng .mq4. Cách duy nhất để phân biệt
chúng là dựa vào vị trí lưu của chúng hoặc bằng cách mở tệp và kiểm tra chúng. Khi
đọc xong cuốn sách này, bạn sẽ có thể xác định được sự khác biệt giữa các loại
chương trình chỉ bằng cách xem mã nguồn. Vị trí tệp Tất cả các tệp MetaEditor được
lưu trữ bên trong thư mục chuyên gia. Thư mục \experts nằm trong thư mục cài đặt
MetaTrader, nằm trong C:\Program Files\. Ví dụ: nếu nhà môi giới của bạn là Interbank
FX, thư mục cài đặt MT4 sẽ là C:\Program Files\Interbank FX Trader 4\. Thư mục
\experts chứa mã nguồn và các tệp thực thi dành cho chuyên gia cố vấn. Sử dụng ví
dụ trên, thư mục \experts sẽ được đặt tại C:\Program Files\Interbank FX Trader
4\experts\. Có rất nhiều thư mục bên trong thư mục \experts chứa các loại mã nguồn
và tệp thực thi khác. Đây là danh sách các vị trí lưu cho tất cả các loại tệp: •
\experts\indicators – Mã nguồn và các tệp thực thi cho các chỉ báo của bạn được lưu
trữ ở đây. • \experts\include – Mã nguồn bao gồm các tệp có phần mở rộng .mqh
được lưu trữ tại đây. • \experts\libraries – Thư viện hàm và DLL được lưu trữ ở đây. •
\experts\scripts – Mã nguồn và các tập tin thực thi cho các tập lệnh được lưu trữ ở
đây. • \experts\templates – Mẫu cho các tập tin mã nguồn được lưu trữ ở đây. Có một
số thư mục khác bên trong thư mục chuyên gia mà bạn cũng cần biết: • \experts\logs
– Nhật ký hoạt động dành cho chuyên gia cố vấn của bạn được lưu trữ tại đây. Những
điều này sẽ hữu ích cho việc gỡ lỗi chuyên gia cố vấn của bạn. • \experts\presets –
Cài đặt Expert Advisor được lưu hoặc tải từ hộp thoại Thuộc tính của MetaTrader
được lưu trữ tại đây. • \experts\files – Bất kỳ tập tin nào được sử dụng cho đầu vào
hoặc đầu ra đều phải được lưu trữ ở đây. 5 www.ZTCprep.com LẬP TRÌNH CỐ VẤN
CHUYÊN GIA MetaEditor MetaEditor là Môi trường phát triển tích hợp (IDE) dành cho
MQL đi kèm với MetaTrader. Nó bao gồm các công cụ tham khảo, tìm kiếm và tự động
hoàn thiện hữu ích giúp việc viết mã trong MQL dễ dàng hơn rất nhiều. Cửa sổ Editor
cho phép bạn mở nhiều tệp cùng một lúc. Bạn có thể thu nhỏ, phóng to và tab giữa
một số cửa sổ đang mở. Cửa sổ Điều hướng cung cấp các tính năng tham khảo và
duyệt tệp hữu ích. Cửa sổ Hộp công cụ hiển thị nội dung trợ giúp, lỗi biên dịch, kết quả
tìm kiếm tệp và truy cập trực tuyến vào các bài viết và tệp tại MQL4.com. Một trong
những tính năng chỉnh sửa hữu ích nhất là Trợ lý. Chỉ cần nhập một vài ký tự đầu tiên
của hàm MQL, toán tử hoặc thành phần ngôn ngữ khác và danh sách thả xuống sẽ
xuất hiện. Nhấn Enter để chấp nhận đề xuất được đánh dấu và tự động hoàn thành
cụm từ. Hình 1.1 – Giao diện MetaEditor. Theo chiều kim đồng hồ từ trên cùng bên
trái: Cửa sổ soạn thảo, cửa sổ Điều hướng và cửa sổ Hộp công cụ. 6
www.ZTCprep.com Giới thiệu về MQL Tab Tệp trong cửa sổ Điều hướng là một trình
duyệt tệp đơn giản cho phép bạn mở và chỉnh sửa bất kỳ tệp MQL nào trong thư mục
\experts của bạn. Tab Từ điển có tham chiếu MQL tích hợp, trong khi tab Tìm kiếm là
tính năng tìm kiếm tham chiếu MQL. Tham chiếu MQL tích hợp và trợ giúp theo ngữ
cảnh sẽ giúp bạn tiết kiệm rất nhiều thời gian khi viết mã. Nếu bạn cần trợ giúp ghi nhớ
cú pháp của một thành phần ngôn ngữ cụ thể, hãy chọn hoặc đặt con trỏ văn bản lên
thành phần đó trong cửa sổ soạn thảo. Nhấn F1 trên bàn phím và chủ đề trợ giúp sẽ
xuất hiện trong cửa sổ Hộp công cụ. Thanh công cụ trong MetaEditor có tính năng bổ
sung tiêu chuẩn cho các chức năng chỉnh sửa và tệp. Các cửa sổ Điều hướng và Hộp
công cụ có thể được hiển thị hoặc ẩn bằng các nút tương ứng trên thanh công cụ.
Hình 1.2 – Tính năng tự động hoàn thành Trợ lý của MetaEditor. Nút Biên dịch biên
dịch tệp hiện tại trong trình chỉnh sửa. Nếu có bất kỳ lỗi biên dịch nào, chúng sẽ được
hiển thị trong cửa sổ Hộp công cụ. Nút Terminal mở terminal giao dịch để thử nghiệm.
Các khái niệm cơ bản Chúng ta sẽ xem xét một số khái niệm lập trình cơ bản để làm
cho phần còn lại của cuốn sách này dễ hiểu hơn đối với các lập trình viên mới. Nếu
bạn là một lập trình viên có kinh nghiệm, vui lòng chuyển sang phần tiếp theo, Bố cục
của tệp MQL. Cú pháp Nếu bạn quen với việc lập trình bằng các ngôn ngữ như C++,
PHP hoặc một trong nhiều ngôn ngữ có cú pháp bắt nguồn từ C, bạn sẽ rất thoải mái
khi lập trình trong MQL. Nếu kinh nghiệm lập trình trước đây của bạn là bằng ngôn ngữ
như Visual Basic thì bạn có thể cần thực hiện một số điều chỉnh. Trong MQL, mọi câu
lệnh đều được kết thúc bằng dấu chấm phẩy. Đây được gọi là một biểu thức. Một biểu
thức có thể kéo dài trên nhiều dòng nhưng phải có dấu chấm phẩy ở cuối. gấp đôi
LastHigh = Cao [1]; string MultiLine = StringConcatenate("Đây là câu lệnh nhiều dòng.
", "Để rõ ràng, chúng tôi sẽ thụt lề nhiều dòng trong cuốn sách này"); 7
www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA Nếu bạn là người mới làm quen
với lập trình hoặc đã quen với việc lập trình bằng ngôn ngữ không kết thúc biểu thức
bằng dấu chấm phẩy, bạn cần đảm bảo rằng bạn đang đặt dấu chấm phẩy ở cuối mọi
tuyên bố. Không kết thúc dòng bằng dấu chấm phẩy là một lỗi phổ biến của người
mới. Có một số trường hợp ngoại lệ: Toán tử ghép không cần dấu chấm phẩy. Toán tử
ghép là một khối mã chứa nhiều biểu thức trong dấu ngoặc nhọn {}. Ví dụ về toán tử
ghép bao gồm toán tử điều khiển (if, switch), toán tử chu trình (for, while) và khai báo
hàm. if(Compound == true) { Print("Đây là biểu thức ghép"); } Lưu ý rằng không có dấu
chấm phẩy sau toán tử if ban đầu và cũng không có dấu chấm phẩy sau dấu ngoặc
nhọn đóng. Tuy nhiên, có dấu chấm phẩy sau hàm Print(). Có thể có một hoặc nhiều
biểu thức bên trong dấu ngoặc nhọn. Mỗi cái phải kết thúc bằng dấu chấm phẩy. Nhận
xét Nhận xét rất hữu ích cho việc ghi lại mã của bạn cũng như để tạm thời xóa mã
trong khi kiểm tra và gỡ lỗi. Bạn có thể nhận xét một dòng bằng hai dấu gạch chéo lên:
// Đây là một nhận xét Một nhận xét nhiều dòng bắt đầu bằng /* và kết thúc bằng */.
Một nhận xét nhiều dòng có thể kéo dài bất kỳ số dòng nào và mọi thứ giữa /* và */
đều được nhận xét. /* Đây là một khối nhận xét Mọi thứ ở đây đều được nhận xét */
Mã định danh Mã định danh là tên được đặt cho các biến và hàm tùy chỉnh. Mã định
danh có thể là sự kết hợp bất kỳ của số, chữ cái và ký tự gạch dưới (_). Mã định danh
có thể dài tới 31 ký tự. Bạn sẽ muốn mã định danh của mình mang tính mô tả chức
năng của chúng, nhưng hãy đảm bảo mã định danh của bạn không khớp với thành
phần ngôn ngữ MQL (còn gọi là từ dành riêng). Đây là ví dụ về mã định danh biến và
mã định danh hàm tùy chỉnh. Mã định danh được in nghiêng: 8 www.ZTCprep.com
Giới thiệu về StopLoss kép MQL; int Order_Count() Mã định danh trong MQL phân biệt
chữ hoa chữ thường. Điều này có nghĩa là StopLoss và stoploss là các biến khác nhau!
Đây là một lỗi phổ biến khác của người mới, vì vậy hãy kiểm tra các tên nhận dạng đó!
Biến Một biến là đơn vị lưu trữ cơ bản của bất kỳ ngôn ngữ lập trình nào. Các biến
chứa dữ liệu cần thiết để chương trình của chúng tôi hoạt động, chẳng hạn như giá cả,
cài đặt và giá trị chỉ báo. Các biến phải được khai báo trước khi sử dụng. Để khai báo
một biến, bạn chỉ định kiểu dữ liệu, mã định danh và giá trị mặc định tùy chọn. Nếu
bạn khai báo một biến nhiều lần hoặc không khai báo một biến nào, bạn sẽ gặp lỗi
biên dịch. Kiểu dữ liệu chỉ định loại thông tin mà biến chứa, cho dù đó là số, chuỗi văn
bản, ngày tháng hay màu sắc. Đây là các kiểu dữ liệu trong MQL: • int – Một số nguyên
(số nguyên) chẳng hạn như 0, 3 hoặc -5. Bất kỳ số nào được gán cho một biến số
nguyên sẽ được làm tròn thành số nguyên tiếp theo. • double – Một số phân số chẳng
hạn như 1,5765, 0,03 hoặc -2,376. Sử dụng chúng cho dữ liệu giá hoặc trong các biểu
thức toán học liên quan đến phép chia. • chuỗi – Một chuỗi văn bản chẳng hạn như
"Con cáo nâu nhanh nhẹn đã nhảy qua con chó lười". Chuỗi phải được bao quanh bởi
dấu ngoặc kép. • boolean – Giá trị đúng/sai. Cũng có thể được biểu diễn dưới dạng 1
(đúng) hoặc 0 (sai). Sử dụng những thứ này bất cứ lúc nào bạn cần để đánh giá điều
kiện nhị phân hoặc điều kiện bật/tắt. • datetime – Giá trị ngày và giờ chẳng hạn như
2009.01.01 00:00. 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. • màu – Một hằng số biểu thị một màu, chẳng hạn
như Đỏ hoặc DarkSlateBlue. Chúng thường được sử dụng để thay đổi màu sắc của chỉ
báo hoặc đối tượng. Đây là một ví dụ về khai báo biến. Đây là một biến số nguyên, có
mã định danh MyVariable và giá trị mặc định là 1. int MyVariable = 1; 9
www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Khi một biến đã được khai báo,
bạn có thể thay đổi giá trị của nó bằng cách gán một giá trị mới cho nó. Đây là một ví
dụ trong đó chúng tôi gán số 5 cho MyVariable: MyVariable = 5; Bạn cũng có thể gán
giá trị của một biến cho một biến khác: int YourVariable = 2; MyVariable =
YourVariable; // MyVariable is 2 Biến được gán phải có cùng kiểu dữ liệu. Ví dụ: nếu
một double được gán cho một biến số nguyên thì double sẽ được làm tròn đến số
nguyên gần nhất. Điều này có thể dẫn đến một kết quả không mong muốn. Hằng số
Đúng như tên gọi của nó, hằng số là một giá trị dữ liệu không bao giờ thay đổi. Ví dụ:
số 5 là hằng số nguyên, chữ 'A' là hằng số ký tự và 2009.01.01 là hằng số ngày giờ cho
ngày 1 tháng 1 năm 2009. MQL có nhiều hằng số tiêu chuẩn cho những thứ như dữ
liệu giá, biểu đồ thời kỳ, màu sắc và hoạt động thương mại. Ví dụ: PERIOD_H1 là hằng
số cho khung thời gian biểu đồ H1, OP_BUY đề cập đến lệnh thị trường mua và Màu đỏ
là hằng số màu cho màu đỏ. Bạn thậm chí có thể tạo các hằng số của riêng mình
bằng cách sử dụng chỉ thị tiền xử lý #define. Chúng ta sẽ sớm đạt được điều đó. Bạn
có thể tìm hiểu thêm về các hằng số tiêu chuẩn của MQL trong phần Hằng số tiêu
chuẩn của Tài liệu tham khảo MQL. Hàm Hàm là khối xây dựng của các ngôn ngữ lập
trình hiện đại. Hàm là một khối mã được thiết kế để thực hiện một nhiệm vụ nhất định,
chẳng hạn như đặt lệnh hoặc tính toán mức dừng lỗ. MQL có hàng tá chức năng tích
hợp sẵn cho mọi thứ, từ chỉ báo kỹ thuật đến đặt lệnh. Các chức năng được thiết kế để
có thể tái sử dụng nhiều lần. Học cách tạo các chức năng cho các tác vụ giao dịch
thông thường là điều cần thiết để lập trình hiệu quả. Chúng ta sẽ nỗ lực tạo ra các hàm
có thể tái sử dụng cho nhiều nhiệm vụ mà chúng ta sẽ học trong cuốn sách này. 10
www.ZTCprep.com Giới thiệu về MQL Hãy bắt đầu với một hàm đơn giản gọi là
PipPoint(), tính toán số điểm thập phân trong cặp hiện tại và tự động điều chỉnh cho
các nhà môi giới 3 và 5 chữ số để kết quả luôn bằng một pip. Đối với cặp Yên (2 hoặc
3 chữ số), hàm trả về 0,01. Đối với tất cả các cặp khác (4 và 5 chữ số), hàm trả về
0,0001. Đây là cách chúng ta gọi hàm từ mã: double UsePoint; UsePoint = PipPoint();
Chúng ta khai báo một biến kiểu double có tên là UsePoint. Sau đó, chúng ta gọi hàm
PipPoint() và gán kết quả cho UsePoint. Ví dụ: bây giờ chúng ta có thể sử dụng giá trị
được lưu trữ trong UsePoint để tính mức dừng lỗ. Đây là mã cho hàm PipPoint():
double PipPoint() { if(Digits == 2 || Digits == 3) double UsePoint = 0,01; khác nếu (Chữ
số == 4 || Chữ số == 5) Điểm sử dụng = 0,0001; return(UsePoint); } Dòng đầu tiên là
phần khai báo hàm của chúng ta. Giống như biến, khai báo hàm cũng có kiểu dữ liệu
và mã định danh. Các hàm sử dụng cùng kiểu dữ liệu như các biến. Kiểu dữ liệu phụ
thuộc vào kiểu dữ liệu mà hàm trả về. Vì hàm này trả về một số phân số nên chúng ta
sử dụng kiểu dữ liệu double. Phần thân của hàm được đặt trong dấu ngoặc vuông {}.
Chúng tôi có câu lệnh if-else đánh giá số chữ số sau vị trí thập phân và gán giá trị
thích hợp cho biến UsePoint. Theo đó, chúng ta có toán tử trả về, trả về giá trị của
UsePoint cho hàm gọi. Có một kiểu dữ liệu đặc biệt cho các hàm không trả về giá trị.
Kiểu dữ liệu void được sử dụng cho các hàm thực hiện một tác vụ cụ thể nhưng không
cần trả về giá trị cho hàm gọi. Hàm void không yêu cầu toán tử trả về trong phần thân
hàm. Hãy xem xét một chức năng đơn giản để đặt lệnh mua. Hàm này có các đối số
cần được truyền vào hàm. Chức năng này sẽ đặt lệnh thị trường mua trên biểu tượng
hiện tại với kích thước lô được chỉ định, dừng lỗ và chốt lãi. 11 www.ZTCprep.com LẬP
TRÌNH CỐ VẤN CHUYÊN GIA int OpenBuyOrder(double LotSize, double StopLoss,
double TakeProfit) { int Ticket =
OrderSend(Symbol(),OP_BUY,LotSize,Ask,StopLoss,TakeProfit); vé khứ hồi); } Hàm này
có ba đối số, LotSize, StopLoss và TakeProfit. Đối số là các biến chỉ được sử dụng
trong hàm. Giá trị của chúng được gán bởi hàm gọi. Đây là cách chúng ta gọi hàm này
trong mã bằng cách sử dụng các hằng số: OpenBuyOrder(2, 1.5550, 1.6050); Điều này
sẽ đặt một lệnh mua gồm 2 lô, với mức dừng lỗ là 1,5550 và mức chốt lời là 1,6050.
Đây là một ví dụ khác sử dụng các biến. Chúng tôi giả định rằng các biến UseLotSize,
BuyStopLoss và BuyTakeProfit có các giá trị phù hợp được gán: int GetTicket =
OpenBuyOrder(UseLotSize,BuyStopLoss,BuyTakeProfit); Trong ví dụ này, chúng ta
đang gán giá trị trả về của OpenBuyOrder() cho biến GetTicket, là số vé của đơn hàng
chúng ta vừa đặt. Việc gán đầu ra của hàm cho một biến là tùy chọn. Trong trường
hợp này, điều này chỉ cần thiết nếu bạn dự định xử lý thêm bằng cách sử dụng số vé
của đơn hàng đã đặt. Các đối số có thể có các giá trị mặc định, có nghĩa là nếu một
tham số không được truyền rõ ràng vào hàm thì đối số sẽ lấy giá trị mặc định. Các đối
số có giá trị mặc định sẽ luôn ở cuối danh sách đối số. Dưới đây là ví dụ về hàm có
nhiều giá trị mặc định: int DefaultValFunc(int Ticket, double Price, int Number = 0,
string Comment = NULL) Hàm này có hai đối số với các giá trị mặc định, Number và
Comment, với các giá trị mặc định là 0 và NULL tương ứng. Nếu muốn sử dụng các giá
trị mặc định cho cả Số và Nhận xét, chúng ta chỉ cần bỏ qua các đối số đó khi gọi
hàm: DefaultValFunc(TicketNum,Useprice); Lưu ý rằng chúng tôi chỉ xác định hai đối
số đầu tiên. Số và Nhận xét sử dụng các giá trị mặc định là 0 và NULL. Nếu chúng ta
muốn chỉ định một giá trị cho Số, nhưng không phải cho Nhận xét, chúng ta chỉ cần bỏ
qua đối số cuối cùng: 12 www.ZTCprep.com Giới thiệu về MQL
DefaultValFunc(TicketNum,Useprice,UseNumber); Một lần nữa, Comment sử dụng giá
trị mặc định là NULL. Tuy nhiên, nếu chúng ta muốn chỉ định một giá trị cho Nhận xét,
bất kể chúng ta có muốn sử dụng giá trị mặc định cho Số hay không, thì chúng ta cũng
phải chỉ định một giá trị cho Số: DefaultValFunc(TicketNum,Useprice,0,"Comment
String" ); Trong ví dụ này, chúng tôi đã sử dụng 0 làm giá trị cho Số, giống với giá trị
mặc định và hằng số chuỗi làm giá trị cho Nhận xét. Hãy nhớ rằng khi bạn đang xử lý
nhiều đối số có giá trị mặc định, bạn chỉ có thể bỏ qua các đối số nếu bạn muốn sử
dụng các giá trị mặc định cho tất cả các đối số còn lại! Phạm vi biến Phạm vi của một
biến xác định chức năng nào nó có sẵn và thời gian lưu trong bộ nhớ. Trong MQL,
phạm vi có thể là cục bộ hoặc toàn cầu. Một biến cục bộ cũng có thể là biến tĩnh. Biến
cục bộ là biến được khai báo bên trong hàm. Các biến cục bộ chỉ có sẵn bên trong
hàm mà nó được khai báo. Biến này được khởi tạo mỗi khi hàm chạy. Khi hàm thoát,
biến và dữ liệu của nó sẽ bị xóa khỏi bộ nhớ. Một ngoại lệ cho điều này sẽ là biến cục
bộ tĩnh. Các biến tĩnh vẫn còn trong bộ nhớ ngay cả sau khi hàm thoát. Khi hàm được
chạy lại, biến không được khởi tạo lại mà thay vào đó vẫn giữ nguyên giá trị trước đó.
Một biến tĩnh được khai báo bằng cách gõ static trước phần khai báo biến. Đây là một
ví dụ về khai báo biến tĩnh: static int MyStaticVar; Nếu một biến tĩnh cần được cung
cấp cho nhiều hàm, thay vào đó hãy sử dụng biến toàn cục. Trong trường hợp này bạn
không cần khai báo biến là tĩnh. Biến toàn cục là biến có sẵn cho tất cả các hàm trong
một chương trình. Miễn là chương trình đang chạy, giá trị của biến toàn cục vẫn được
duy trì. Các biến toàn cục được khai báo bên ngoài hàm, thường ở đầu tệp mã nguồn.
13 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Không có phương pháp đặc
biệt nào để khởi tạo một biến toàn cục. Cú pháp giống hệt cú pháp của một biến cục
bộ. Bố cục của tệp MQ4 Tạo một Expert Advisor mới Trình hướng dẫn Expert Advisor
trong MetaEditor là cách nhanh nhất để bắt đầu tạo một Expert Advisor. Bạn có thể
khởi động trình hướng dẫn bằng cách chọn Mới từ menu Tệp, bằng cách nhấn nút Mới
trên thanh công cụ hoặc bằng cách nhấn Ctrl+N trên bàn phím. Hộp thoại trình bày
cho bạn một số tùy chọn. Bạn có thể tạo chỉ báo, tập lệnh, thư viện và bao gồm các
tệp bằng trình hướng dẫn. Bạn cũng có thể chọn mẫu để tạo tệp. Tệp kết quả sẽ được
lưu vào thư mục thích hợp, tùy thuộc vào loại của nó. Đảm bảo Expert Advisor được
chọn và nhấn Next. Hình 1.3 – Thuộc tính chung của Expert Advisor Wizard. 14
www.ZTCprep.com Giới thiệu về MQL Bạn sẽ được nhắc nhập Tên, Tác giả và Liên kết,
cũng như một số tham số tùy chọn. Trường Tên sẽ là tên tệp của chương trình của
bạn. EA sẽ được lưu vào thư mục \experts dưới tên tệp đó. Nội dung của trường Tác
giả sẽ xuất hiện bên cạnh tên EA trong Trình kiểm tra chiến lược và dưới dạng chú giải
công cụ khi bạn di chuột qua tên EA trong cửa sổ Điều hướng. Trường Liên kết là một
URL tới trang web của bạn nhưng nó sẽ không xuất hiện ở bất kỳ đâu ngoài tệp mã
nguồn. Bạn cũng có thể nhập các thông số giao dịch của mình tại đây. Hiện tại, hãy
thêm một hoặc hai tham số nhưng đừng bận tâm đến việc điều chỉnh chúng. Tốt nhất
là bạn chỉ cần thêm chúng vào mã nguồn một cách thủ công sau này. Nhấn nút Hoàn
tất và mẫu cố vấn chuyên gia sẽ mở ra với thông tin của bạn đã được thêm vào. Mẫu
Expert Advisor mặc định khá tối giản nhưng chứa cấu trúc cơ bản của một Expert
Advisor. Hãy xác định bố cục của tệp MQL bằng cách sử dụng mẫu Expert Advisor làm
hướng dẫn của chúng tôi. Chỉ thị tiền xử lý Điều đầu tiên xuất hiện trong bất kỳ tệp
MQL nào là các chỉ thị tiền xử lý. Chúng được mở đầu bằng một #. Mẫu Expert Advisor
mặc định có hai: #property Copyright, là tên Tác giả bạn đã nhập trong Expert Advisor
Wizard và liên kết #property, là Liên kết bạn đã nhập trong Wizard. Có các chỉ thị
#property khác, nhưng hầu hết chúng đều liên quan đến chỉ báo và tập lệnh. Chỉ thị
#property duy nhất bạn nên đưa vào cố vấn chuyên môn của mình là #property bản
quyền, chỉ thị này xác định EA là sản phẩm của bạn. Loại chỉ thị tiền xử lý thứ hai mà
bạn có thể sẽ sử dụng là chỉ thị #include. Như đã đề cập trước đó, tệp đính kèm bao
gồm các hàm và mã nguồn sẽ được đưa vào dự án của bạn khi nó được biên dịch. Cú
pháp của lệnh include là: #include Tệp stdlib.mqh trong ví dụ của chúng tôi ở trang 19
là tệp bao gồm tiêu chuẩn đi kèm với MetaTrader. Nó bao gồm một số chức năng linh
tinh mà các lập trình viên có thể thấy hữu ích. Giống như tất cả các tệp bao gồm, nó
nằm trong thư mục \experts\include. Lệnh #define được sử dụng để khai báo các
hằng số sử dụng trong chương trình của chúng ta. Ví dụ: thay vì gõ ra một chuỗi văn
bản dài mỗi khi bạn cần sử dụng nó, bạn có thể xác định một hằng số và nhập nó: 15
www.ZTCprep.com CHƯƠNG TRÌNH TƯ VẤN CHUYÊN NGHIỆP #define MYCONSTANT
“Đây là một hằng số” Trong ví dụ này , chúng ta có thể sử dụng mã định danh không
đổi MYCONSTANT thay cho chuỗi văn bản trong mã của chúng ta. Quy ước cho các
mã định danh không đổi là sử dụng tất cả các chữ cái viết hoa. Mặc dù điều này
không thực sự cần thiết nhưng để đảm bảo tính nhất quán, bạn nên xác định tất cả
các mã định danh cho các hằng số bằng cách sử dụng chữ hoa. Đôi khi, một hàm bạn
cần sử dụng đã được biên dịch trong một tệp khác, chẳng hạn như một chuyên gia cố
vấn khác, tệp thư viện (.ex4) hoặc tệp Windows DLL (.dll). Bạn có thể nhập hàm trực
tiếp vào dự án bằng cách sử dụng chỉ thị #import. Các thư viện tương tự như các tệp
bao gồm, nhưng thay vì đưa mã nguồn vào dự án của chúng ta, chúng ta sẽ thực thi
tệp khác và gọi hàm từ tệp đó. Chúng ta sẽ nói về việc sử dụng thư viện ở phần sau
của cuốn sách. Lệnh nhập thường được đặt trong các tệp bao gồm, đặc biệt nếu có
nhiều hàm cần nhập. Nhưng nếu bạn chỉ cần nhập một hoặc hai hàm và tệp đính kèm
cho chúng chưa tồn tại thì hãy tiếp tục và nhập chúng trực tiếp vào dự án của bạn. Để
biết ví dụ chi tiết về lệnh #import, hãy xem trang Tham chiếu MQL Nhập hàm và xem
các tệp đính kèm trong thư mục \experts\include. Đây là cú pháp cho lệnh #import:
#import "library.ex4" double MyImportedFunction(); #import Trong ví dụ này, tệp thư
viện mà chúng ta đang nhập (các) hàm từ đó là thư viện.ex4. Chúng tôi đang nhập
một hàm duy nhất thuộc loại double, được gọi là MyImportedFunction(). Mã định danh
hàm phải khớp với tên hàm trong tệp thư viện nguồn. Lưu ý dấu chấm phẩy ở cuối
phần khai báo hàm. Tham số và Biến bên ngoài Phần tiếp theo trong tệp mã nguồn cố
vấn chuyên gia của chúng tôi là các biến bên ngoài. Đây là những thông số có thể điều
chỉnh cho hệ thống giao dịch của chúng tôi. Điều này bao gồm cài đặt giao dịch của
bạn (dừng lỗ, chốt lãi, kích thước lô) và cài đặt chỉ báo. Khi bạn mở hộp thoại Thuộc
tính Chuyên gia dành cho cố vấn chuyên gia, bạn đang xem các biến bên ngoài cho
chương trình đó. Chúng ta chỉ định một biến ngoài bằng cách thêm extern vào trước
biến. Điều này chỉ định rằng biến sẽ xuất hiện trong hộp thoại Thuộc tính chuyên gia và
người dùng sẽ có thể xem và điều chỉnh. 16 www.ZTCprep.com Giới thiệu về MQL
extern double StopLoss = 50; Hãy chắc chắn rằng mã định danh cho biến bên ngoài
của bạn mang tính mô tả về chức năng thực sự của biến đó. ("StopLoss" tốt hơn "stop"
hoặc "SL" chẳng hạn). Bạn có 31 ký tự để mô tả biến của mình, vì vậy hãy tận dụng tối
đa ký tự đó. Giá trị mặc định cho biến của bạn cũng sẽ là giá trị mặc định cho tham số
đó, vì vậy hãy chọn giá trị mặc định logic. Biến toàn cục Chúng tôi khai báo bất kỳ biến
toàn cục nào ở đầu tệp mã nguồn, thường là sau các biến bên ngoài. Vị trí không quan
trọng, miễn là cả biến toàn cục và biến bên ngoài đều được đặt bên ngoài và trước bất
kỳ hàm nào. Biến toàn cục là biến có sẵn cho bất kỳ chức năng nào trong chương
trình. Miễn là chương trình đang chạy, biến toàn cục và giá trị của nó vẫn còn trong bộ
nhớ và có thể được tham chiếu và thay đổi bởi bất kỳ chức năng nào trong chương
trình. Về mặt kỹ thuật, các biến bên ngoài cũng mang tính toàn cục, nhưng các biến
toàn cục mà chúng ta đang thảo luận trong phần này là nội bộ, có nghĩa là người dùng
không thể xem hoặc thay đổi chúng. Các hàm đặc biệt MQL có 3 hàm dựng sẵn để
điều khiển việc thực hiện chương trình: init(), deinit() và start(). Hàm init() bao gồm mã
được chạy một lần khi EA được khởi động lần đầu tiên. Hàm init() là tùy chọn và có thể
bỏ qua nếu bạn không sử dụng. Hàm deinit() bao gồm mã được chạy một lần khi EA
dừng. Chức năng này cũng là tùy chọn và có thể bạn sẽ không cần phải sử dụng nó với
chuyên gia cố vấn. Hàm start() chứa mã chương trình chính và được yêu cầu trong EA
của bạn. Mỗi khi chức năng bắt đầu được chạy, các điều kiện giao dịch của bạn sẽ
được kiểm tra và các lệnh được đặt hoặc đóng tùy thuộc vào cách đánh giá các điều
kiện đó. Hàm start() được chạy theo từng tích tắc. Dấu tích là sự biến động giá hoặc
thay đổi giá Mua hoặc Giá bán của một cặp tiền tệ. Trong thời gian thị trường hoạt
động, có thể có vài tích tắc mỗi giây. Trong thời gian thị trường chậm lại, nhiều phút có
thể trôi qua mà không có tích tắc. 17 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN
GIA Các chức năng khác Bất kỳ chức năng nào khác mà EA của bạn có thể sử dụng
phải được khai báo sau hàm start(). Các hàm này sẽ được gọi từ các hàm start(), init()
hoặc deinit() hoặc từ các hàm khác được gọi từ chương trình chính. Chúng tôi sẽ đề
cập đến các chức năng tùy chỉnh ở phần sau của cuốn sách. 18 www.ZTCprep.com
Giới thiệu về MQL // Chỉ thị tiền xử lý #property bản quyền "Andrew Young" #property
link "http://www.expertadvisorbook.com" #include #define MYCONSTANT "Đây là
hằng số" // Tham số bên ngoài extern int Parameter1 = 1; bên ngoài gấp đôi Tham số2
= 0,01; // Biến toàn cục int GlobalVariable1; // Hàm khởi tạo int init() { // Mã khởi động
return(0); } // Hàm Deinit int deinit() { // Mã tắt máy return(0); } // Hàm bắt đầu int
start() { // Mã chính return(0); } // Hàm tùy chỉnh int MyCustomFunction() { // Mã tùy
chỉnh return(0); } Hình 1.4 – Bố cục chuyên gia cố vấn mẫu 19 www.ZTCprep.com LẬP
TRÌNH CHUYÊN GIA CỐ VẤN Chương 2 Đặt lệnh Giá thầu, Hỏi & Chênh lệch Là một
nhà giao dịch ngoại hối, bạn có thể đã quen thuộc với giá Mua và Bán. Nhưng bạn có
thể không nhận thức được vai trò của họ trong việc sắp xếp đơn hàng. Điều rất quan
trọng là sử dụng giá chính xác khi mở hoặc đóng lệnh. Giá Bid là giá bạn thấy trên biểu
đồ MetaTrader. Đó thường là những gì chúng ta nghĩ đến khi nghĩ về “giá hiện tại”. Giá
Hỏi thường chỉ cao hơn giá Mua vài pip. Sự khác biệt giữa Giá mua và Giá bán là mức
chênh lệch giá, tức là hoa hồng của nhà môi giới cho việc đặt lệnh. Giá Hỏi là nơi
chúng ta mở lệnh mua và đóng lệnh bán. Giá Bid là nơi chúng ta mở lệnh bán và đóng
lệnh mua. Bạn sẽ cần chỉ ra mức giá chính xác khi mở lệnh thị trường hoặc khi đóng
lệnh tại thị trường, vì vậy hãy nhớ sự khác biệt giữa hai giá này. Các loại lệnh Có ba loại
lệnh có thể được đặt trong MetaTrader: lệnh thị trường, lệnh dừng và lệnh giới hạn.
Lệnh thị trường là phổ biến nhất. Lệnh thị trường sẽ mở một vị thế ngay lập tức ở mức
giá Mua hoặc Bán hiện hành. Khi đặt lệnh thị trường trong MQL, chúng ta phải chỉ định
giá mở cửa (thường là báo giá Mua hoặc Bán mới nhất). Nếu giá mở cửa được chỉ
định đã lỗi thời, do thị trường chuyển động nhanh hoặc do sự chậm trễ trong việc thực
hiện chương trình, thiết bị đầu cuối sẽ cố gắng đặt lệnh ở mức giá thị trường hiện tại,
miễn là nó nằm trong mức trượt giá tối đa. Nếu bạn đặt lệnh thị trường bằng hộp thoại
Lệnh mới trong MetaTrader, bạn sẽ thấy cài đặt ở dưới cùng có nhãn "Cho phép độ
lệch tối đa so với giá niêm yết". Khi điều này được chọn, bạn có thể chỉ định độ lệch tối
đa tính bằng pip. Đây là mức trượt tối đa. Nếu giá hiện tại nằm ngoài giá mở cửa được
chỉ định của chúng tôi, cộng hoặc trừ độ trượt, lỗi báo giá lại sẽ xảy ra và lệnh sẽ
không được đặt. Bạn có thể nhận thấy điều này khi cố gắng đặt lệnh thị trường trong
thời điểm thị trường chuyển động nhanh. Lưu ý rằng các nhà môi giới ECN/STP không
sử dụng cài đặt trượt giá và sẽ luôn mở lệnh thị trường ở mức giá hiện tại. 20
www.ZTCprep.com Đặt lệnh Lệnh dừng là một loại lệnh chờ. Lệnh chờ là yêu cầu mở
lệnh thị trường ở một mức giá nhất định. Lệnh dừng mua được đặt trên giá hiện tại,
trong khi lệnh dừng bán được đặt dưới giá hiện tại. Kỳ vọng là giá cuối cùng sẽ tăng
hoặc giảm đến mức đó và tiếp tục theo hướng đó, mang lại lợi nhuận. Lệnh giới hạn
ngược lại với lệnh dừng. Lệnh giới hạn mua được đặt dưới giá hiện tại, trong khi lệnh
giới hạn bán được đặt trên giá hiện tại. Kỳ vọng là giá sẽ tăng hoặc giảm đến mức đó,
kích hoạt lệnh và sau đó đảo ngược. Lệnh giới hạn không được sử dụng thường xuyên
trong giao dịch tự động. Thời gian hết hạn có thể được đặt cho các lệnh đang chờ xử
lý. Nếu lệnh không được thực hiện trước thời gian hết hạn, lệnh sẽ tự động bị xóa.
Không phải tất cả các nhà môi giới đều hỗ trợ hết hạn giao dịch. Quy trình đặt hàng
Quá trình đặt hàng trong MQL bao gồm một số bước. Chúng ta phải xác định những
điều sau trước khi đặt lệnh: • Loại lệnh được đặt – mua hoặc bán; dừng lại, thị trường
hoặc giới hạn. • Cặp tiền tệ để giao dịch – nói chung là biểu đồ mà EA được đính kèm.
• Kích thước lô hàng. Đây có thể là kích thước lô cố định hoặc kích thước được tính
bằng quy trình quản lý tiền. • Giá mở lệnh. Đối với lệnh thị trường, đây sẽ là giá Mua
hoặc Bán hiện tại. Đối với các lệnh chờ, giá mở cửa phải cách giá hiện tại một khoảng
tối thiểu và phải 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. • Giá
dừng lỗ. Điểm dừng lỗ có thể là mức giá được xác định trước, giá trị chỉ báo, số pip cố
định so với giá mở lệnh hoặc có thể được tính toán linh hoạt bằng cách sử dụng quy
trình quản lý rủi ro. Lệnh dừng lỗ có thể được đặt cùng với lệnh hoặc có thể được thêm
vào lệnh sau đó. • Giá chốt lời. Đây thường là một số pip cố định tính từ giá mở lệnh,
mặc dù nó cũng có thể được tính bằng các phương pháp khác. Lợi nhuận có thể được
đặt cùng với đơn đặt hàng hoặc có thể được thêm vào đơn hàng sau đó. • Thông tin
nhận dạng đơn hàng chẳng hạn như nhận xét đơn hàng hoặc "con số kỳ diệu" xác định
đơn hàng được đặt bởi một cố vấn chuyên gia cụ thể. • Giá hết hạn tùy chọn cho các
lệnh chờ, nếu nhà môi giới hỗ trợ. 21 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN
GIA OrderSend() Hàm OrderSend() được sử dụng để đặt hàng trong MQL. Cú pháp
như sau: int OrderSend(string Symbol, int Type, double Lots, double Price, int Slippage,
double StopLoss, double TakeProfit, string Comment = NULL, int MagicNumber = 0,
datetime Expiration = 0, color Arrow = CLR_NONE) ; • Ký hiệu – Một chuỗi đại diện cho
cặp tiền tệ để giao dịch, ví dụ GBPUSD. Hàm Symbol() được sử dụng cho cặp tiền tệ
của biểu đồ hiện tại. • Loại – Loại lệnh đặt: mua hoặc bán; thị trường, dừng lại hoặc
giới hạn. Đây là một giá trị số nguyên, được biểu thị bằng các hằng số sau: ◦ OP_BUY –
Lệnh mua thị trường (giá trị số nguyên 0). ◦ OP_SELL – Lệnh bán thị trường (giá trị
nguyên 1). ◦ OP_BUYSTOP – Lệnh dừng mua (giá trị nguyên 2). ◦ OP_SELLSTOP –
Lệnh dừng bán (giá trị nguyên 3). ◦ OP_BUYLIMIT – Lệnh giới hạn mua (giá trị nguyên
4). ◦ OP_SELLLIMIT – Lệnh giới hạn bán (giá trị nguyên 5). • Lot – Số lượng Lot để giao
dịch. Bạn có thể chỉ định lô nhỏ (0,1) hoặc lô siêu nhỏ (0,01) nếu nhà môi giới của bạn
hỗ trợ. • Price – Giá mở lệnh. Đối với lệnh mua thị trường, đây sẽ là lệnh Hỏi. Đối với
lệnh thị trường bán, đây sẽ là Giá thầu. Đối với các lệnh chờ, đây sẽ là bất kỳ mức giá
hợp lệ nào cao hơn hoặc thấp hơn giá hiện tại. • Trượt – Mức trượt điểm tối đa. Sử
dụng cài đặt đủ lớn khi giao dịch tự động. Các sàn không sử dụng trượt giá sẽ bỏ qua
thông số này. • StopLoss – Giá dừng lỗ. Đối với lệnh mua, giá dừng lỗ thấp hơn giá mở
lệnh và đối với lệnh bán thì cao hơn. Nếu được đặt thành 0, lệnh dừng lỗ sẽ không
được sử dụng. • TakeProfit – Giá chốt lời. Đối với lệnh mua, mức chốt lời sẽ cao hơn
giá mở lệnh và đối với lệnh bán là thấp hơn. Nếu được đặt thành 0, sẽ không có lợi
nhuận nào được sử dụng. • Bình luận – Một chuỗi tùy chọn sẽ phục vụ như một bình
luận về đơn hàng. Nhận xét được hiển thị dưới tab Giao dịch trong cửa sổ Terminal.
Nhận xét đơn hàng cũng có thể được sử dụng như một mã định danh đơn hàng. 22
www.ZTCprep.com Đặt hàng • MagicNumber – Một giá trị số nguyên tùy chọn sẽ xác
định đơn hàng được đặt bởi một cố vấn chuyên gia cụ thể. Chúng tôi khuyên bạn nên
sử dụng cái này. • Hết hạn – Thời gian hết hạn tùy chọn cho các lệnh đang chờ xử lý.
Không phải tất cả các nhà môi giới đều chấp nhận thời gian hết hạn giao dịch – đối với
những nhà môi giới này, sẽ xảy ra lỗi nếu chỉ định thời gian hết hạn. • Mũi tên – Màu
tùy chọn cho mũi tên sẽ được vẽ trên biểu đồ, cho biết giá và thời gian mở cửa. Nếu
không chỉ định màu, mũi tên sẽ không được vẽ. Hàm OrderSend() trả về số phiếu của
đơn hàng vừa được đặt. Nếu không có đơn hàng nào được đặt, do tình trạng lỗi, giá trị
trả về sẽ là -1. Chúng ta có thể lưu phiếu đặt hàng vào biến toàn cục hoặc biến tĩnh để
sử dụng sau. Nếu đơn hàng không được đặt do tình trạng lỗi, chúng tôi có thể phân
tích lỗi và thực hiện hành động thích hợp dựa trên mã lỗi được trả về. Đặt lệnh thị
trường Đây là một ví dụ về lệnh mua thị trường. Chúng tôi giả định rằng các biến
LotSize, Slippage, BuyStopLoss, BuyTakeProfit và MagicNumber đã được tính toán
hoặc chỉ định và hợp lệ.
OrderSend(Symbol(),OP_BUY,LotSize,Ask,Slippage,BuyStopLoss,BuyTakeProfit, "Mua
đơn hàng",MagicNumber,0,Green); Hàm Symbol() trả về biểu tượng biểu đồ hiện tại.
Chúng tôi sẽ đặt lệnh trên cặp biểu đồ hiện tại trong 99% thời gian. OP_BUY chỉ ra rằng
đây là lệnh thị trường mua. Hỏi là một biến được xác định trước trong MQL để lưu trữ
báo giá Hỏi gần đây nhất. (Hãy nhớ rằng lệnh mua sẽ mở ở mức giá Bán!) Độ trượt
được thiết lập bằng cách sử dụng biến bên ngoài. Tham số trượt giá là một số nguyên,
biểu thị số điểm cho phép trượt giá. Nếu nhà môi giới của bạn sử dụng báo giá có 4
chữ số (2 cho cặp Yên), 1 điểm sẽ bằng 1 pip. Tuy nhiên, nếu nhà môi giới của bạn đưa
ra báo giá 3 và 5 chữ số thì 1 điểm sẽ là 0,1 pip. Trong trường hợp này, bạn cần thêm
số 0 bổ sung vào cuối cài đặt Trượt giá của mình. Chúng tôi đã thêm nhận xét chung
"Đơn đặt hàng mua" vào đơn hàng này. Vì không có thời hạn đối với lệnh thị trường nên
tham số Hết hạn là 0. Cuối cùng, chúng tôi chỉ định hằng số màu Xanh lục để vẽ mũi
tên màu xanh lục trên biểu đồ. Dưới đây là ví dụ về lệnh bán trên thị trường, sử dụng
các tham số tương tự như trên: 23 www.ZTCprep.com CHƯƠNG TRÌNH TƯ VẤN
CHUYÊN NGHIỆP
OrderSend(Symbol(),OP_SELL,LotSize,Bid,Slippage,SellStopLoss,SellTakeProfit, "Sell
Order",MagicNumber ,0,Đỏ); Chúng tôi sử dụng OP_SELL làm loại lệnh để chỉ định lệnh
bán trên thị trường. Chúng tôi sử dụng Giá thầu làm giá mở lệnh, để phản ánh thực tế
là lệnh bán mở ở giá Mua. "Lệnh bán" là nhận xét về lệnh của chúng tôi và chúng tôi sử
dụng Màu đỏ làm màu mũi tên để phân biệt với lệnh mua. Đặt lệnh dừng chờ xử lý Sự
khác biệt giữa lệnh chờ xử lý và lệnh thị trường là giá mở lệnh sẽ khác với giá thị
trường hiện tại. Giá trị dừng lỗ và chốt lời phải được tính tương ứng với giá mở lệnh
chờ xử lý. Trong các ví dụ này, chúng tôi sẽ sử dụng biến PendingPrice cho giá lệnh
chờ xử lý của mình. Nó có thể được tính toán dựa trên thuật toán giao dịch của chúng
tôi hoặc có thể được đặt làm tham số bên ngoài. Đối với lệnh dừng mua, Giá chờ xử lý
phải lớn hơn giá Hỏi hiện tại. Chúng tôi giả định rằng BuyStopLoss và BuyTakeProfit đã
được tính toán chính xác so với PendingPrice. Dưới đây là ví dụ về vị trí đặt lệnh dừng
mua: OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice,Slippage,BuyStopLoss,
BuyTakeProfit,"Buy Stop Order",MagicNumber,0,Green); Lưu ý rằng chúng tôi sử dụng
OP_BUYSTOP để biểu thị lệnh dừng mua và PendingPrice cho giá mở lệnh của chúng
tôi. Không có thời gian hết hạn đã được chỉ định cho lệnh này. Đối với lệnh dừng bán,
Giá chờ xử lý phải thấp hơn giá Mua hiện tại. Trong ví dụ này, chúng tôi sẽ thêm thời
gian hết hạn của đơn hàng bằng cách sử dụng biến Hết hạn. Thời gian hết hạn phải
lớn hơn thời gian máy chủ hiện tại. Dưới đây là ví dụ về vị trí đặt lệnh dừng bán:
OrderSend(Symbol(),OP_SELLSTOP,LotSize,PendingPrice,Slippage,SellStopLoss,
SellTakeProfit,"Sell Stop Order",MagicNumber,Expiration,Red); Việc đặt lệnh Giới hạn
lệnh chờ xử lý cũng tương tự như lệnh dừng, ngoại trừ giá lệnh chờ xử lý bị đảo ngược
so với giá hiện tại và loại lệnh. Đối với lệnh giới hạn mua, giá lệnh chờ phải thấp hơn
giá Mua hiện tại. Dưới đây là ví dụ về lệnh giới hạn mua: 24 www.ZTCprep.com Đặt
lệnh Đặt lệnh Gửi(Symbol(),OP_BUYLIMIT,LotSize,PendingPrice,Slippage,BuyStopLoss,
BuyTakeProfit,"Buy Limit Order",MagicNumber,0,Green); Lưu ý rằng chúng tôi đã sử
dụng OP_BUYLIMIT để biểu thị lệnh giới hạn mua. Mặt khác, các thông số của chúng
tôi giống hệt với các thông số dành cho lệnh dừng. Đối với lệnh giới hạn bán, giá lệnh
chờ phải lớn hơn giá Hỏi hiện tại. Dưới đây là ví dụ về lệnh giới hạn bán:
OrderSend(Symbol(),OP_SELLLIMIT,LotSize,PendingPrice,Slippage,SellStopLoss,
SellTakeProfit,"Sell Limit Order",MagicNumber,Expiration,Red); Tính mức dừng lỗ và
chốt lời Có một số cách tính giá dừng lỗ và chốt lời. Phương pháp phổ biến nhất là chỉ
định số pip tính từ giá mở lệnh để đặt điểm dừng của bạn. Ví dụ: nếu chúng tôi cài đặt
mức dừng lỗ là 50 pip, điều đó có nghĩa là giá dừng lỗ sẽ cách giá mở lệnh của chúng
tôi là 50 pip. Chúng ta cũng có thể sử dụng giá trị chỉ báo, tham số bên ngoài hoặc
một số loại tính toán giá khác. Tất cả những gì chúng ta cần làm sau đó là xác minh
rằng giá dừng lỗ hoặc chốt lãi là hợp lệ. Tính bằng Pips Đối với phương pháp tính điểm
dừng phổ biến nhất này, chúng tôi sẽ sử dụng một biến bên ngoài trong đó người dùng
chỉ định số pip cho mức dừng lỗ và chốt lãi. Sau đó chúng tôi tính toán các điểm dừng
liên quan đến giá mở lệnh. Đối với lệnh thị trường mua, giá mở cửa sẽ là Giá bán và đối
với lệnh thị trường bán, giá mở cửa sẽ là Giá thầu. Đối với các lệnh dừng và giới hạn
đang chờ xử lý, chúng tôi chỉ định giá mở cửa hợp lệ khác với giá thị trường hiện tại.
Chúng ta sẽ gán mức giá phù hợp cho biến OpenPrice. Dưới đây là các biến bên ngoài
mà chúng tôi sẽ sử dụng cho cài đặt dừng lỗ và chốt lời: extern int StopLoss = 50;
extern int TakeProfit = 100; 25 www.ZTCprep.com CHƯƠNG TRÌNH CỐ VẤN CHUYÊN
GIA Trong ví dụ này, chúng ta đã nhập mức dừng lỗ là 50 pip và chốt lời là 100 pip. Bạn
có thể đã thấy các cài đặt tương tự như vậy trong EA mà bạn đã sử dụng. Để tính điểm
dừng lỗ, chúng ta cần cộng hoặc trừ 50 pip từ giá mở lệnh. Trước tiên, chúng ta cần
chuyển đổi giá trị nguyên 50 thành giá trị phân số mà chúng ta sẽ sử dụng để cộng
hoặc trừ giá mở cửa. Đối với cặp Yên, 50 pips bằng 0,50. Đối với tất cả các cặp khác,
nó là 0,0050. Để chuyển đổi một số nguyên thành giá trị phân số thích hợp, chúng ta
cần nhân biến StopLoss bên ngoài với Điểm. Point Point là một biến được xác định
trước trong MQL trả về đơn vị giá nhỏ nhất của một loại tiền tệ, tùy thuộc vào số chữ
số thập phân. Đối với cặp tiền tệ có 4 chữ số thập phân, điểm là 0,0001. Đối với một
cặp Yên, nó là 0,01. Hãy tính mức dừng lỗ cho lệnh thị trường mua. Chúng tôi sẽ chỉ
định giá Bán hiện tại cho OpenPrice và sử dụng giá đó làm giá mở lệnh của chúng tôi.
Chúng tôi sẽ kiểm tra xem cài đặt StopLoss của chúng tôi có lớn hơn 0 hay không.
Nếu vậy, chúng tôi sẽ nhân StopLoss với Điểm. Sau đó, chúng tôi sẽ trừ số tiền đó khỏi
OpenPrice. Kết quả sẽ được lưu trữ trong biến BuyStopLoss. gấp đôi OpenPrice = Hỏi;
if(StopLoss > 0) double BuyStopLoss = OpenPrice – (StopLoss * Point); // 1,4600 - (50
* 0,0001) = 1,4550 Nếu StopLoss không lớn hơn 0 thì BuyStopLoss được khởi tạo với
giá trị 0 và sẽ không có lệnh dừng lỗ nào được đặt cùng với lệnh. Giả sử Điểm đó bằng
0,0001, nếu giá mở lệnh là 1,4600 và mức dừng lỗ của chúng tôi là 50 pip thì giá dừng
lỗ cho lệnh mua sẽ là 1,4600 - (0,0050) = 1,4550. Gần đây, nhiều nhà môi giới đang
chuyển sang báo giá pip phân số, với 3 chữ số thập phân cho các cặp Yên và 5 chữ số
thập phân cho tất cả các cặp khác. Nếu nhà môi giới của chúng tôi sử dụng báo giá
pip phân số thì trong ví dụ trên của chúng tôi, Điểm sẽ bằng 0,00001. Nếu chúng tôi sử
dụng giá trị điểm là 0,00001 trong ví dụ tính toán mức dừng lỗ ở trên thì mức dừng lỗ
sẽ được tính là 5 pip so với giá mở cửa, thay vì 50 pip. Điều này đặt ra một vấn đề. Để
có được giá trị chính xác, chúng tôi sẽ phải thêm số 0 bổ sung vào cài đặt dừng lỗ của
mình – tức là StopLoss = 500. Thay vì yêu cầu người dùng thêm số 0 bổ sung vào mức
dừng lỗ của họ và nhận cài đặt lợi nhuận mỗi khi họ giao dịch với tỷ lệ nhỏ môi giới pip,
chúng tôi sẽ tạo một hàm luôn trả về 0,01 hoặc 0,0001, 26 www.ZTCprep.com Đặt
lệnh bất kể nhà môi giới có sử dụng pip phân số hay không. Chúng ta sẽ gọi hàm này
là PipPoint, vì nó sẽ luôn trả về giá trị điểm bằng một 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); } Đối số chuỗi Tiền tệ là ký hiệu của cặp tiền tệ mà chúng ta
muốn lấy điểm. Hàm MarketInfo() với tham số MODE_DIGITS trả về số vị trí thập phân
(chữ số) cho cặp đó. Câu lệnh if-else gán giá trị điểm thích hợp cho biến CalcPoint, tùy
thuộc vào số chữ số. Đây là một ví dụ về việc sử dụng chức năng này. Bạn sẽ sử dụng
cặp biểu đồ hiện tại trong phần lớn thời gian, vì vậy chúng ta sẽ chuyển hàm Symbol()
làm đối số. Điều này sẽ trả về điểm cho biểu đồ hiện tại. gấp đôi UsePoint =
PipPoint(Symbol()); Sau đây là tập hợp các ví dụ sử dụng các cặp cụ thể: double
UsePoint = PipPoint(EURUSD); // Kết quả là 0,0001 double UsePoint =
PipPoint(USDJPY); // Kết quả là 0,01 Chúng ta sẽ sử dụng hàm này để tìm giá trị điểm
pip duy nhất cho phần còn lại của cuốn sách này. Như chúng tôi đã trình bày, biến
Điểm sẽ không hoạt động chính xác trên các công ty môi giới pip phân số khi tính giá
trị của một pip đơn lẻ. Bạn không bao giờ có thể cho rằng EA sẽ chỉ được sử dụng trên
nhà môi giới 2 và 4 chữ số, do đó cần phải tự động xác định giá trị điểm của một pip
bằng cách sử dụng PipPoint(). Trượt và Điểm Hãy lạc đề trong một phút và tạo một
hàm để thay đổi kích thước tham số trượt cho phù hợp. Như đã đề cập trước đó trong
chương này, đối với một nhà môi giới có báo giá pip phân số, tham số trượt giá cho
hàm OrderSend() sẽ cần phải tăng lên gấp 10 lần để có được giá trị trượt giá chính
xác. 27 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN NGHIỆP Chức năng này sẽ
tự động thiết lập tham số trượt giá thành số pip được chỉ định bởi tham số Trượt giá
bên ngoài: 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); } Chúng ta chuyển ký hiệu tiền tệ và tham số
trượt giá bên ngoài làm đối số. Nếu loại tiền tệ sử dụng dấu ngoặc kép có 2 hoặc 4
chữ số, chúng tôi sẽ sử dụng đối số SlippagePips không thay đổi làm cài đặt trượt giá
của mình. Nếu loại tiền tệ sử dụng dấu ngoặc kép có 3 hoặc 5 chữ số, chúng tôi nhân
SlippagePips với 10. Đây là cách chúng tôi sử dụng hàm này trong OrderSend(): //
Tham số bên ngoài extern int Slippage = 5; // Đặt lệnh
OrderSend(Symbol(),OP_BUY,LotSize,Ask,GetSlippage(Symbol(),Slippage),BuyStopLoss,
BuyTakeProfit,"Mua lệnh",MagicNumber,0,Green); Độ trượt trong ví dụ này sẽ là 5 pip
và tham số trượt sẽ được tự động điều chỉnh dựa trên số chữ số trong báo giá tiền tệ.
Độ trượt và Điểm dưới dạng các biến toàn cục Nhược điểm của việc sử dụng hàm để
trả về giá trị điểm hoặc độ trượt là phải gõ thêm các đối số của hàm. Chúng tôi sẽ tạo
các biến toàn cục sẽ giữ các giá trị điểm và độ trượt thích hợp cho cặp tiền tệ của
chúng tôi và chúng tôi sẽ sử dụng các biến đó bất cứ lúc nào chúng tôi cần để tham
chiếu các giá trị đó. Vì các giá trị này sẽ không bao giờ thay đổi trong quá trình thực
thi chương trình nên chúng ta sẽ tính toán các giá trị này trong hàm init(). Chúng ta giả
định rằng biến số nguyên bên ngoài Slippage đã có sẵn: // Biến toàn cục double
UsePoint; int UseTrượt trượt; int init() { UsePoint = PipPoint(Symbol()); UseSlippage =
GetSlippage(Symbol(),Slippage); } 28 www.ZTCprep.com Đặt hàng Từ giờ trở đi, chúng
tôi sẽ sử dụng UsePoint và UseSlippage để chỉ các giá trị này. Đoạn mã trên giả định
rằng EA của bạn chỉ đặt hàng trên một loại tiền tệ. Trường hợp này xảy ra trong 98%
trường hợp, nhưng nếu bạn đang tạo một chuyên gia tư vấn đặt lệnh trên nhiều loại
tiền tệ (hoặc trên một loại tiền tệ khác với biểu đồ hiện tại), bạn sẽ cần sử dụng
PipPoint() và GetSlippage () hoạt động mỗi khi bạn cần tính các giá trị này.
MarketInfo() Chúng tôi đã sử dụng hàm MarketInfo() ở trên để truy xuất giá trị Điểm và
số chữ số trong báo giá tiền tệ. Hàm MarketInfo() có nhiều cách sử dụng và bạn sẽ sử
dụng nó để truy xuất thông tin giá cần thiết trong chương trình của mình. Đây là cú
pháp của hàm MarketInfo(): double MarketInfo(string Symbol, int requestType); Đối số
Ký hiệu chỉ đơn giản là ký hiệu tiền tệ mà bạn muốn truy xuất thông tin. Đối với biểu
tượng biểu đồ hiện tại, có thể sử dụng hàm Symbol(). Đối với các ký hiệu khác, bạn sẽ
cần chỉ định ký hiệu tiền tệ, chẳng hạn như EURJPY. Loại yêu cầu là một hằng số
nguyên, biểu thị thông tin mà bạn đang yêu cầu từ hàm. Đây là danh sách các hằng số
MarketInfo() hữu ích nhất. Bạn có thể tìm thấy danh sách đầy đủ trong Tài liệu tham
khảo MQL, trong Hằng số tiêu chuẩn – MarketInfo. • MODE_POINT – Giá trị điểm. Ví
dụ: 0,01 hoặc 0,00001. • MODE_DIGITS – Số chữ số thập phân trong giá. Sẽ là 2 hoặc
3 cho các cặp Yên và 4 hoặc 5 cho tất cả các cặp khác. • MODE_SPREAD – Mức
chênh lệch hiện tại. Ví dụ: 3 pip (hoặc 30 đối với nhà môi giới pip phân đoạn). •
MODE_STOPLEVEL – Mức dừng. Ví dụ: 3 pip (hoặc 30 đối với nhà môi giới pip phân
đoạn). Những mã định danh yêu cầu này thường được sử dụng khi kiểm tra thông tin
giá trên một loại tiền tệ khác hoặc bất kỳ nơi nào mà biểu tượng có thể là bất kỳ thứ gì
khác ngoài biểu tượng biểu đồ hiện tại: • MODE_BID – Giá thầu hiện tại của biểu tượng
đã chọn. • MODE_ASK – Giá chào bán hiện tại của mã đã chọn. • MODE_LOW – Mức
thấp của thanh hiện tại của biểu tượng đã chọn. • MODE_HIGH – Mức cao của thanh
hiện tại của biểu tượng đã chọn. 29 www.ZTCprep.com CHƯƠNG TRÌNH CỐ VẤN
CHUYÊN GIA Tính toán mức dừng lỗ Bây giờ chúng ta đã xác định được giá trị điểm
thích hợp, đã đến lúc tính mức dừng lỗ của chúng ta. Đối với lệnh mua, mức dừng lỗ
sẽ thấp hơn giá mở lệnh và đối với lệnh bán, mức dừng lỗ sẽ cao hơn giá mở lệnh. Đây
là phép tính dừng lỗ của lệnh mua của chúng tôi trước đó, có thêm biến UsePoint. Lưu
ý rằng chúng tôi đã gán giá Bán cho biến OpenPrice: double OpenPrice = Hỏi;
if(StopLoss > 0) double BuyStopLoss = OpenPrice – (StopLoss * UsePoint); Và đây là
cách tính cho một lệnh bán. Lưu ý rằng chúng tôi đã chỉ định Giá thầu cho OpenPrice
và chúng tôi chỉ cộng thay vì trừ: double OpenPrice = Bid; if(StopLoss > 0) double
SellStopLoss = OpenPrice + (StopLoss * UsePoint); Đối với các lệnh chờ, mức dừng lỗ
sẽ được tính tương ứng với giá của lệnh chờ. Trong trường hợp này, hãy sử dụng biến
OpenPrice để lưu trữ giá lệnh chờ thay vì giá thị trường hiện tại. Logic sẽ giống hệt với
các ví dụ trên. Tính mức chốt lời Tính giá chốt lãi tương tự như tính toán mức dừng lỗ,
ngoại trừ việc chúng ta sẽ đảo ngược phép cộng và phép trừ. Đối với lệnh mua, giá
chốt lãi sẽ cao hơn giá mở lệnh và đối với lệnh bán, giá chốt lãi sẽ thấp hơn giá mở
lệnh. Chúng tôi giả định rằng mức giá phù hợp đã được chỉ định cho OpenPrice:
if(TakeProfit > 0) double BuyTakeProfit = OpenPrice + (TakeProfit * UsePoint);
if(TakeProfit > 0) double SellTakeProfit = OpenPrice - (TakeProfit * UsePoint); Các
phương pháp dừng lỗ thay thế Có nhiều cách khác để xác định mức dừng lỗ và chốt
lời. Ví dụ: mức cao hoặc mức thấp gần đây hoặc giá trị chỉ báo có thể được sử dụng
để xác định mức dừng lỗ. Hãy chứng minh làm thế nào chúng ta có thể tính toán
những điều này. 30 www.ZTCprep.com Đặt lệnh Giả sử chúng ta đang sử dụng một hệ
thống giao dịch đặt mức dừng lỗ 2 pip dưới mức thấp của thanh hiện tại. Chúng tôi sử
dụng mảng giá được xác định trước Thấp[] để truy xuất mức thấp của thanh. Thấp[0]
là mức thấp của thanh hiện tại, Thấp[1] là mức thấp của thanh trước đó, v.v. Sau khi
xác định mức thấp của thanh hiện tại, chúng tôi nhân 2 với UsePoint để nhận giá trị
thập phân và trừ giá trị đó khỏi mức thấp: double BuyStopLoss = Low[0] – (2 *
UsePoint); Vì vậy, nếu mức thấp của thanh là 1,4760 thì mức dừng lỗ sẽ được đặt ở
mức 1,4758. Nhưng có thể bạn muốn đặt mức dừng lỗ của mình ở mức thấp nhất
trong số x thanh cuối cùng. Có một chức năng được tích hợp trong MetaTrader chỉ
dành cho việc đó. iLowest() trả về giá trị dịch chuyển cho biết thanh có giá trị thấp
nhất trong khoảng thời gian đã chỉ định. Chúng ta có thể sử dụng các giá trị cao, thấp,
mở hoặc đóng. Đây là ví dụ về cách chúng ta sử dụng iLowest() để tìm mức thấp nhất
trong 10 thanh cuối cùng: int CountBars = 10; int LowestShift =
iLowest(NULL,0,MODE_LOW,CountBars,0); gấp đôi BuyStopLoss = Thấp[LowestShift];
Tham số đầu tiên của iLowest() là ký hiệu tiền tệ – NULL có nghĩa là chúng ta đang sử
dụng ký hiệu hiện tại. Nhiều hàm trong MQL sử dụng hằng chuỗi NULL để chỉ biểu
tượng biểu đồ hiện tại. Tham số thứ hai là chu kỳ biểu đồ – 0 đề cập đến khung biểu
đồ hiện tại. MODE_LOW là hằng số nguyên chỉ định mảng chuỗi giá thấp. Nói cách
khác, chúng tôi đang tìm kiếm mức thấp nhất trong số CountBars cuối cùng. Ví dụ:
nếu chúng tôi muốn tìm mức đóng thấp nhất, chúng tôi sẽ sử dụng MODE_CLOSE. Bạn
có thể tìm thấy tất cả các hằng số mảng chuỗi trong Tham chiếu MQL trong Hằng số
tiêu chuẩn – Mảng chuỗi. CountBars là số thanh chúng tôi muốn tìm kiếm, trong
trường hợp này là 10. Cuối cùng, tham số cuối cùng là vị trí bắt đầu của chúng tôi. 0 là
thanh hiện tại. Để bắt đầu ở thanh trước đó, hãy đếm ngược từ thanh hiện tại – thanh
trước đó là 1, thanh trước đó là 2, v.v. Đầu ra của hàm iLowest() là một số nguyên biểu
thị sự dịch chuyển lùi của thanh có mức thấp nhất giá trị trong chuỗi giá. Trong ví dụ
trên, nếu iLowest() trả về số 6, điều đó có nghĩa là mức thấp nhất thấp nhất là 6 vạch.
Chúng tôi lưu trữ giá trị đó trong biến LowestShift. Để tìm giá thực tế, chúng ta chỉ cần
lấy giá trị của Low[LowestShift], hay nói cách khác là Low[6]. 31 www.ZTCprep.com
LẬP TRÌNH TƯ VẤN CHUYÊN GIA Nếu bạn muốn tính mức dừng lỗ cho một lệnh bán
bằng phương pháp này, hàm iHighest() cũng hoạt động theo cách tương tự. Tham
khảo ví dụ trên, bạn sẽ sử dụng MODE_HIGH cho tham số mảng chuỗi của mình. Đây
là một ví dụ sử dụng một chỉ báo. Giả sử chúng ta có một đường trung bình động và
chúng ta muốn sử dụng đường trung bình động làm điểm dừng lỗ. Chúng ta sẽ sử
dụng biến MA để biểu thị giá trị trung bình động của thanh hiện tại. Tất cả những gì
bạn cần làm là gán giá trị trung bình động hiện tại cho mức dừng lỗ: double
BuyStopLoss = MA; Nếu đường trung bình động hiện ở mức 1,6894 thì đó sẽ là điểm
dừng lỗ của chúng tôi. Đây đơn giản là những phương pháp phổ biến nhất để xác định
mức dừng lỗ hoặc chốt lãi. Các phương pháp khác có thể được phát triển bằng cách
sử dụng kiến ​thức phân tích kỹ thuật hoặc trí tưởng tượng của bạn. Truy xuất thông tin
đơn hàng Khi chúng tôi đã đặt hàng thành công, chúng tôi sẽ cần truy xuất một số
thông tin về đơn hàng nếu chúng tôi muốn sửa đổi hoặc đóng nó. Chúng tôi thực hiện
việc này bằng hàm OrderSelect(). Để sử dụng OrderSelect(), chúng ta có thể sử dụng
số phiếu của đơn hàng hoặc có thể lặp qua nhóm các lệnh đang mở và chọn từng lệnh
theo thứ tự. Sau khi chọn lệnh bằng OrderSelect(), chúng ta có thể sử dụng nhiều hàm
thông tin lệnh khác nhau để trả về thông tin về lệnh, bao gồm mức dừng lỗ hiện tại,
chốt lời, giá mở lệnh, giá đóng, v.v. OrderSelect() Đây là cú pháp của hàm
OrderSelect(): bool OrderSelect(int Index, int Select, int Pool = MODE_TRADES) • Index
– Đây là số vé của lệnh mà chúng ta muốn chọn hoặc vị trí trong nhóm đơn đặt hàng.
Tham số Chọn sẽ cho biết chúng tôi đang sử dụng tham số nào trong số này. • Chọn –
Một hằng số cho biết tham số Chỉ mục là số phiếu hay vị trí nhóm đơn đặt hàng: ◦
SELECT_BY_TICKET – Giá trị của tham số Chỉ mục là số phiếu đặt hàng. ◦
SELECT_BY_POS – Giá trị của tham số Chỉ mục là vị trí nhóm đơn hàng. 32
www.ZTCprep.com Đặt lệnh • Nhóm – Một hằng số tùy chọn cho biết nhóm lệnh: lệnh
chờ/mở hoặc lệnh đã đóng. ◦ MODE_TRADES – Theo mặc định, chúng tôi đang kiểm
tra nhóm lệnh hiện đang mở. ◦ MODE_HISTORY – Kiểm tra nhóm đơn hàng đã đóng
(lịch sử đơn hàng). Nếu hàm OrderSelect() định vị đơn hàng thành công thì giá trị trả
về sẽ là true, nếu không thì giá trị trả về sẽ là false. Đây là ví dụ về hàm OrderSelect()
sử dụng số phiếu đặt hàng. Biến Vé phải chứa một vé đặt hàng hợp lệ:
OrderSelect(Ticket,SELECT_BY_TICKET); Sau khi hàm OrderSelect() được gọi, chúng
ta có thể sử dụng bất kỳ hàm thông tin đơn hàng nào để lấy thông tin về đơn hàng đó.
Bạn có thể tìm thấy danh sách đầy đủ các chức năng có thể được sử dụng với
OrderSelect() trong Tham chiếu MQL trong Chức năng giao dịch. Đây là danh sách các
hàm thông tin lệnh được sử dụng phổ biến nhất: • ​OrderSymbol() – Ký hiệu của công
cụ mà lệnh đã chọn được đặt trên đó. • OrderType() - Loại lệnh của lệnh đã chọn: mua
hoặc bán; thị trường, dừng lại hoặc giới hạn. Giá trị trả về là một số nguyên tương ứng
với hằng số loại lệnh ở trang 22. • OrderOpenPrice() – Giá mở lệnh của lệnh đã chọn. •
OrderLots() – Kích thước lô của lệnh đã chọn. • OrderStopLoss() – Giá dừng lỗ của
lệnh đã chọn. • OrderTakeProfit() – Giá chốt lời của lệnh đã chọn. • OrderTicket() – Số
vé của đơn hàng đã chọn. Thường được sử dụng khi duyệt qua nhóm đơn đặt hàng với
thông số SELECT_BY_POS. • OrderMagicNumber() – Con số kỳ diệu của thứ tự đã
chọn. Khi duyệt qua các đơn đặt hàng, bạn sẽ cần sử dụng thông tin này để xác định
các đơn đặt hàng do EA của bạn đặt. • OrderComment() – Bình luận được đặt cùng với
đơn hàng. Điều này có thể được sử dụng như một mã định danh thứ tự phụ. •
OrderClosePrice() – Giá đóng của lệnh đã chọn. Đơn hàng phải được đóng (tức là có
trong nhóm lịch sử đơn hàng). • OrderOpenTime() – Thời gian mở lệnh đã chọn. •
OrderCloseTime() – Thời gian đóng lệnh đã chọn. 33 www.ZTCprep.com LẬP TRÌNH
TƯ VẤN CHUYÊN GIA • OrderProfit() – Trả về lợi nhuận (bằng loại tiền gửi) cho lệnh đã
chọn. Chúng ta sẽ cần sử dụng OrderSelect() trước khi đóng hoặc sửa đổi đơn hàng.
Hãy minh họa cách chúng ta sử dụng OrderSelect() để đóng một lệnh. Đóng lệnh Khi
chúng ta đóng lệnh thị trường, chúng ta sẽ thoát giao dịch ở mức giá thị trường hiện
tại. Đối với lệnh mua, chúng tôi đóng ở mức giá Mua và đối với lệnh bán, chúng tôi
đóng ở mức Hỏi. Đối với các lệnh đang chờ xử lý, chúng tôi chỉ cần xóa lệnh khỏi nhóm
giao dịch. OrderClose() Chúng tôi đóng các lệnh thị trường bằng hàm OrderClose().
Đây là cú pháp: bool OrderClose(int Ticket, double Lots, double Price, int Slippage,
color Arrow); • Ticket – Số vé của lệnh thị trường đóng. • Lots – Số lượng lot cần đóng.
Hầu hết các nhà môi giới cho phép đóng một phần. • Giá – Giá ưu tiên để đóng giao
dịch. Đối với lệnh mua, đây sẽ là giá Mua hiện tại và đối với lệnh bán, đây sẽ là giá Bán
hiện tại. • Trượt giá – Độ trượt giá cho phép so với giá đóng cửa, tính bằng pip. • Màu
sắc – Một hằng số màu cho mũi tên đóng. Nếu không có màu nào được chỉ định, sẽ
không có mũi tên nào được vẽ. Bạn có thể đóng một phần giao dịch bằng cách chỉ
định kích thước lô một phần. Ví dụ: nếu bạn mở giao dịch với kích thước lô là 2,00 và
bạn muốn đóng một nửa giao dịch thì chỉ định 1 lô cho đối số Lô. Lưu ý rằng không
phải tất cả các nhà môi giới đều hỗ trợ đóng một phần. Khuyến nghị rằng nếu bạn cần
đóng một vị thế ở nhiều phần, bạn nên đặt nhiều lệnh thay vì đóng một phần. Sử dụng
ví dụ trên, bạn sẽ đặt hai lệnh, mỗi lệnh 1,00 lô, sau đó chỉ cần đóng một trong các
lệnh khi bạn muốn đóng một nửa vị thế. Trong cuốn sách này, chúng tôi sẽ luôn kết
thúc đơn hàng đầy đủ. Ví dụ sau đóng lệnh thị trường mua:
OrderSelect(CloseTicket,SELECT_BY_TICKET); 34 www.ZTCprep.com Đặt hàng
if(OrderCloseTime() == 0 && OrderType() == OP_BUY) { double CloseLots =
OrderLots(); Giá đóng gấp đôi = Giá thầu; bool Đã đóng =
OrderClose(CloseTicket,CloseLots,ClosePrice,UseSlippage,Red); } Biến CloseTicket là
số phiếu của đơn hàng mà chúng ta muốn đóng. Hàm OrderSelect() chọn đơn hàng và
cho phép chúng ta truy xuất thông tin đơn hàng. Chúng ta sử dụng OrderCloseTime()
để kiểm tra thời gian đóng lệnh xem lệnh đã được đóng chưa. Nếu OrderCloseTime()
trả về 0 thì chúng ta biết đơn hàng vẫn chưa được đóng. Chúng ta cũng cần kiểm tra
loại lệnh vì loại lệnh xác định giá đóng của lệnh. Hàm OrderType() trả về một số
nguyên cho biết loại đơn hàng. Nếu đó là lệnh thị trường mua, được biểu thị bằng
OP_BUY, chúng tôi sẽ tiếp tục đóng lệnh. Tiếp theo, chúng tôi truy xuất kích thước lô
đơn hàng bằng OrderLots() và lưu trữ giá trị đó trong CloseLots. Chúng tôi chỉ định Giá
thầu hiện tại cho ClosePrice. Sau đó, chúng ta gọi hàm OrderClose() để đóng đơn
hàng. Chúng tôi chỉ định cài đặt Trượt giá bằng UseSlippage và chỉ ra Mũi tên màu đỏ
sẽ được in trên biểu đồ. Giá trị trả về boolean được lưu trữ trong biến Closed. Nếu lệnh
đã được đóng thành công thì giá trị Closed sẽ là true, ngược lại là false. Để đóng lệnh
thị trường bán, tất cả những gì bạn cần làm là thay đổi loại lệnh thành OP_SELL và gán
giá Bán hiện tại cho ClosePrice: if(OrderCloseTime() == 0 && OrderType() == OP_SELL)
{ double CloseLots = OrderLots( ); gấp đôi Giá đóng cửa = Hỏi; bool Đã đóng =
OrderClose(CloseTicket,CloseLots,ClosePrice,UseSlippage,Red); } OrderDelete() Có
một chức năng riêng để đóng các lệnh đang chờ xử lý. OrderDelete() có hai đối số, số
vé và màu mũi tên. Không yêu cầu giá đóng cửa, kích thước lô hoặc độ trượt giá. Đây
là mã để đóng một lệnh dừng mua đang chờ xử lý: 35 www.ZTCprep.com CHƯƠNG
TRÌNH TƯ VẤN CHUYÊN NGHIỆP OrderSelect(CloseTicket,SELECT_BY_TICKET);
if(OrderCloseTime() == 0 && OrderType() == OP_BUYSTOP) { bool Đã xóa =
OrderDelete(CloseTicket,Red); } Như chúng ta đã làm với hàm OrderClose() ở trên,
chúng ta cần kiểm tra loại lệnh để chắc chắn rằng đó là lệnh chờ xử lý. Các hằng số
loại lệnh chờ xử lý là OP_BUYSTOP, OP_SELLSTOP, OP_BUYLIMIT và OP_SELLLIMIT.
Để đóng các loại lệnh chờ khác, chỉ cần thay đổi loại lệnh. Nếu lệnh đã được thực hiện
thì bây giờ nó là lệnh thị trường và thay vào đó phải được đóng bằng OrderClose().
Expert Advisor đơn giản Hãy xem mã mà chúng ta đã thảo luận cho đến nay sẽ hoạt
động như thế nào trong Expert Advisor. Đây là một hệ thống chéo trung bình di chuyển
đơn giản. Lệnh mua được mở khi đường trung bình động 10 kỳ lớn hơn đường trung
bình động 20 kỳ. Khi đường trung bình động 10 kỳ nhỏ hơn đường trung bình động 20
kỳ, lệnh bán sẽ được mở. EA này sẽ luân phiên giữa việc mở lệnh mua và bán. Lệnh sẽ
bị đóng khi lệnh được mở theo hướng ngược lại, hoặc bằng cách dừng lỗ hoặc chốt
lời. Chúng ta sẽ sử dụng các biến toàn cục BuyTicket và SellTicket để lưu trữ phiếu đặt
hàng cuối cùng. Khi một lệnh mới được mở, phiếu lệnh cuối cùng sẽ bị xóa. Điều này
ngăn cản việc mở nhiều lệnh liên tiếp. #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; 36 www.ZTCprep.com Đặt hàng gấp đôi UsePoint; 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); // 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); 37 www.ZTCprep.com LẬP TRÌNH CỐ
VẤN CHUYÊN GIA 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; } 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); } Chúng tôi bắt đầu với chỉ thị tiền xử lý bản
quyền #property xác định mã thuộc về chúng tôi. Tiếp theo là các biến bên ngoài và có
thể tự giải thích được. Chúng tôi khai báo BuyTicket và SellTicket là các biến toàn cục
- theo cách này, phiếu đặt hàng được lưu trữ giữa các lần thực hiện chương trình.
Chúng ta cũng có thể khai báo chúng dưới dạng biến tĩnh trong hàm start(). Chúng tôi
thêm UsePoint và UseSlippage làm biến toàn cục – chúng tôi sẽ tính giá trị của các
biến này tiếp theo. Hàm init() của chúng ta được chạy đầu tiên. Chúng tôi gọi các hàm
PipPoint() và GetSlippage() (được khai báo ở phần cuối của tệp Vị trí đặt hàng 38
www.ZTCprep.com) và gán các giá trị trả về cho các biến toàn cục của chúng tôi.
Chúng tôi sẽ sử dụng những giá trị này khi tham chiếu các giá trị điểm hoặc độ trượt
giá trong phần còn lại của chuyên gia cố vấn của chúng tôi. Tiếp theo là hàm start(),
hàm thực thi chương trình chính của chúng ta. Chúng tôi đã bỏ qua deinit(), vì chúng
tôi không sử dụng nó ở đây. Hàm iMA() tính toán đường trung bình động Biến FastMA
chứa đường trung bình động 10 kỳ của chúng ta, được thiết lập bằng biến
FastMAPeriod. Biến SlowMA là đường trung bình động 20 kỳ của chúng tôi, được đặt
bằng SlowMAPeriod. Mọi thứ khác được đặt thành mặc định (không có sự thay đổi,
đường trung bình động đơn giản được tính dựa trên giá đóng cửa). Chúng ta sử dụng
toán tử if để xác định các điều kiện mở lệnh. Nếu đường trung bình động 10 kỳ hiện tại
(FastMA) lớn hơn đường trung bình động 20 kỳ (SlowMA) và nếu BuyTicket bằng 0,
chúng ta sẽ mở lệnh mua. Trước khi mở lệnh mua, chúng ta sẽ đóng lệnh bán hiện tại
nếu có. Chúng tôi sử dụng OrderSelect() để truy xuất SellTicket hiện tại. Nếu thời gian
đóng lệnh là 0 (cho biết lệnh chưa được đóng) và SellTicket lớn hơn 0 (cho biết
SellTicket có khả năng hợp lệ), chúng tôi sẽ tiếp tục và đóng lệnh bán. Chúng tôi truy
xuất kích thước lô của lệnh bán và giá Bán hiện tại, đây sẽ là giá đóng cửa cho lệnh
bán. Sau đó, chúng ta đóng lệnh bán bằng OrderClose(). Tiếp theo, chúng tôi gán giá
Bán hiện tại cho biến Openprice – đây sẽ là giá mở lệnh mua của chúng tôi. Chúng tôi
tính toán mức dừng lỗ và chốt lãi tương ứng với giá mở cửa, trước tiên hãy kiểm tra để
đảm bảo rằng chúng tôi đã chỉ định giá trị StopLoss hoặc TakeProfit trong cài đặt. Sau
đó, chúng ta đặt hàng bằng hàm OrderSend() và lưu phiếu đặt hàng trong BuyTicket.
Cuối cùng, chúng tôi xóa giá trị của SellTicket, cho phép đặt một lệnh bán khác khi
điều kiện đơn hàng trở nên hợp lệ. Khối lệnh bán tuân theo logic tương tự như khối
lệnh mua. Trước tiên, chúng tôi đóng lệnh mua và sử dụng Giá thầu làm OpenPrice và
lệnh mua ClosePrice. Việc tính toán mức dừng lỗ và chốt lời bị đảo ngược. Hàm start()
kết thúc bằng toán tử trả về. Các hàm PipPoint() và GetSlippage() tùy chỉnh của chúng
tôi được xác định ở cuối sau hàm start(). Chúng tôi sẽ đưa các hàm này vào mọi ví dụ
trong cuốn sách này. Sử dụng các lệnh chờ Hãy sửa đổi EA của chúng tôi để sử dụng
các lệnh chờ. Chúng tôi sẽ sử dụng lệnh dừng trong ví dụ này. Khi đường trung bình
động nhanh lớn hơn đường trung bình động chậm, chúng ta sẽ đặt lệnh dừng mua 10
pip 39 www.ZTCprep.com CHƯƠNG TRÌNH TƯ VẤN CHUYÊN NGHIỆP trên mức cao
hiện tại. Khi điều ngược lại là đúng, chúng ta sẽ đặt lệnh dừng bán thấp hơn mức thấp
hiện tại 10 pip. Hãy khai báo một biến bên ngoài để điều chỉnh cài đặt này, được gọi là
PendingPips. extern int PendingPips = 10; Chúng tôi đang thêm hàm OrderDelete()
vào khối lệnh mua và bán để đóng mọi lệnh đang chờ xử lý chưa được thực hiện.
Chúng ta cần kiểm tra loại lệnh của lệnh được chỉ định bởi SellTicket để đảm bảo rằng
chúng ta đang sử dụng đúng chức năng để đóng lệnh.
OrderSelect(BánTicket,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); if(Đóng == true)
SellTicket = 0; } // Xóa đơn hàng khác if(OrderCloseTime() == 0 && SellTicket > 0 &&
OrderType() == OP_SELLSTOP) { bool Đã xóa = OrderDelete(SellTicket,Red); if(Đã xóa
== true) SellTicket = 0; } Chúng tôi sử dụng OrderType() để kiểm tra xem lệnh bán đã
chọn là lệnh thị trường hay lệnh dừng. Nếu đó là lệnh thị trường, chúng tôi sẽ đóng nó
bằng OrderClose(). Nếu đó là một lệnh đang chờ xử lý, chúng tôi sẽ đóng nó bằng
OrderDelete(). Đây là cách tính giá lệnh chờ xử lý của chúng tôi. Chúng tôi chỉ cần
chuyển đổi PendingPips thành giá trị phân số bằng UsePoint và thêm nó vào giá Đóng
hiện tại. Chúng tôi sẽ lưu trữ giá trị này trong biến PendingPrice. Tiếp theo, chúng tôi
tính toán mức dừng lỗ và chốt lời tương ứng với giá lệnh chờ xử lý của chúng tôi. Cuối
cùng, chúng tôi đặt lệnh chờ xử lý bằng cách sử dụng OrderSend(), lưu trữ kết quả
giao dịch trong biến BuyTicket: double PendingPrice = Close[0] + (PendingPips *
UsePoint); if(StopLoss > 0) double BuyStopLoss = Giá đang chờ xử lý - (StopLoss *
UsePoint); if(TakeProfit > 0) double BuyTakeProfit = Đang chờ xử lý + (TakeProfit *
UsePoint); BuyTicket =
OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice,UseSlippage,
BuyStopLoss,BuyTakeProfit,"Lệnh dừng mua",MagicNumber,0,Green); 40
www.ZTCprep.com Đặt lệnh SellTicket = 0; Mã bên dưới hiển thị các thay đổi đối với
khối lệnh dừng bán: OrderSelect(BuyTicket,SELECT_BY_TICKET); // Đóng đơn hàng
if(OrderCloseTime() == 0 && BuyTicket > 0 && OrderType() == OP_BUY) { CloseLots =
OrderLots(); Giá đóng cửa = Giá thầu; Đã đóng =
OrderClose(MuaTicket,CloseLots,ClosePrice,UseSlippage,Red); if(Đóng == true)
MuaTicket = 0; } // Xóa đơn hàng khác if(OrderCloseTime() == 0 && BuyTicket > 0 &&
OrderType() == OP_BUYSTOP) { Closed = OrderDelete(BuyTicket,Red); if(Đóng == true)
MuaTicket = 0; } PendingPrice = Close[0] - (PendingPips * UsePoint); gấp đôi
SellStopLoss = Giá đang chờ xử lý + (StopLoss * UsePoint); gấp đôi SellTakeProfit =
Giá đ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; Mã
hoàn chỉnh cho cả hai chuyên gia cố vấn này nằm trong Phụ lục A. 41
www.ZTCprep.com CHƯƠNG TRÌNH CHUYÊN GIA CỐ VẤN CHUYÊN GIA Chương 3 Vị
trí đặt lệnh nâng cao Khả năng tương thích ECN Như các ví dụ về vị trí đặt hàng trong
chương trước cho thấy, phương pháp mặc định để đặt mức dừng lỗ và chốt lời với lệnh
thị trường là đặt chúng bằng hàm OrderSend(). Mặc dù điều này hoạt động tốt với hầu
hết các nhà môi giới, nhưng các nhà môi giới ECN/STP mới hơn sử dụng MetaTrader
không hỗ trợ hành vi này. Trong trường hợp này, chúng ta sẽ cần đặt mức dừng lỗ và
chốt lời sau khi đặt lệnh bằng cách sử dụng hàm OrderModify(). Điều này chỉ áp dụng
cho các lệnh thị trường – đối với các lệnh đang chờ xử lý, bạn vẫn có thể đặt mức
dừng lỗ và chốt lãi bằng hàm OrderSend(). Sửa đổi lệnh Sau khi đặt lệnh, bạn có thể
sửa đổi mức chốt lời, dừng lỗ, giá lệnh chờ hoặc thời gian hết hạn bằng cách sử dụng
hàm OrderModify(). Để sử dụng OrderModify(), chúng ta cần số phiếu của đơn hàng
mà chúng ta muốn sửa đổi. Đây là cú pháp của hàm OrderModify(): bool
OrderModify(int Ticket, double Price, double StopLoss, double TakeProfit, datetime
Expiration, color Arrow = CLR_NONE) • Ticket – Số vé của lệnh cần sửa đổi. • Price –
Giá lệnh chờ mới. • StopLoss – Giá dừng lỗ mới. • TakeProfit – Giá chốt lời mới. • Hết
hạn – Thời gian hết hạn mới cho các lệnh đang chờ xử lý. • Mũi tên – Màu tùy chọn
cho mũi tên để biểu thị thứ tự được sửa đổi. Nếu không được chỉ định, sẽ không có
mũi tên nào được hiển thị. Nếu việc sửa đổi đơn hàng thành công, OrderModify() sẽ
trả về giá trị boolean là true. Nếu sửa đổi đơn hàng không thành công, giá trị trả về sẽ
sai. 42 www.ZTCprep.com Vị trí đặt hàng nâng cao Khi sửa đổi đơn đặt hàng, chúng ta
phải chắc chắn rằng các giá trị chúng ta truyền cho hàm là hợp lệ. Ví dụ: lệnh vẫn phải
mở - chúng tôi không thể sửa đổi lệnh đã đóng. Khi sửa đổi các lệnh chờ bằng tham
số Giá, lệnh đó phải chưa được khớp – tức là đạt giá đặt lệnh. Giá lệnh sửa đổi cũng
không được quá gần với giá Mua hoặc Bán hiện tại. Chúng ta cũng nên kiểm tra để
đảm bảo rằng lệnh dừng lỗ và chốt lời là hợp lệ. Chúng ta có thể thực hiện việc này
bằng cách sử dụng quy trình xác minh giá mà chúng ta sẽ đề cập ở phần sau của
chương này. Nếu chúng ta không sửa đổi một tham số cụ thể, chúng ta phải chuyển
giá trị ban đầu cho hàm OrderModify(). Ví dụ: nếu chúng ta chỉ sửa đổi mức dừng lỗ
cho một lệnh đang chờ xử lý thì chúng ta phải truy xuất giá lệnh hiện tại và chốt lãi
bằng cách sử dụng OrderSelect() và chuyển các giá trị đó cho hàm OrderModify(). Nếu
bạn cố gắng sửa đổi đơn hàng mà không chỉ định bất kỳ giá trị đã thay đổi nào, bạn sẽ
gặp lỗi 1: "không có kết quả". Bạn nên xác minh lý do tại sao mã của bạn truyền các
giá trị không thay đổi cho hàm, nhưng nếu không thì lỗi này vô hại và có thể được bỏ
qua một cách an toàn. Thêm mức dừng lỗ và chốt lời vào lệnh hiện tại Trước tiên,
chúng ta cần xác minh rằng lệnh đã được đặt chính xác. Chúng ta thực hiện điều này
bằng cách kiểm tra giá trị trả về của hàm OrderSend(), là số phiếu của đơn hàng vừa
được đặt. Nếu đơn hàng không được đặt do tình trạng lỗi, số vé sẽ bằng -1. Tiếp theo,
chúng ta sử dụng hàm OrderSelect() để lấy thông tin về đơn hàng vừa đặt. Chúng ta sẽ
sử dụng các hàm OrderOpenPrice(), OrderTakeProfit(), OrderStopLoss() và tùy chọn
OrderExpiration() khi chuyển các giá trị không thay đổi cho hàm OrderModify(). Cuối
cùng, chúng ta sẽ sử dụng OrderModify() để thêm mức dừng lỗ và chốt lời cho lệnh.
Đây là ví dụ trong đó chúng tôi đặt mức dừng lỗ và chốt lời cho lệnh mua bằng hàm
OrderModify(). Chúng tôi đã di chuyển tính toán dừng lỗ và chốt lãi sau hàm
OrderSend() để nó được tính toán trước khi chúng tôi sửa đổi đơn hàng: int BuyTicket
= OrderSend(Symbol(),OP_BUY,LotSize,Ask,UseSlippage,0,0, "Lệnh mua",
MagicNumber, 0, Green); if(BuyTicket > 0) {
OrderSelect(BuyTicket,SELECT_BY_TICKET); gấp đôi OpenPrice = OrderOpenPrice();
if(StopLoss > 0) double BuyStopLoss = OpenPrice – (StopLoss * UsePoint);
if(TakeProfit > 0) double BuyTakeProfit = OpenPrice + (TakeProfit * UsePoint); 43
www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA if(BuyStopLoss > 0 ||
BuyTakeProfit > 0) { bool TicketMod =
OrderModify(BuyTicket,OrderOpenPrice(),BuyStopLoss, BuyTakeProfit,0); } } Hàm
OrderSend() giống với ví dụ trước của chúng tôi, ngoại trừ việc chúng tôi sử dụng giá
trị 0 cho các tham số dừng lỗ và chốt lời. Giá trị bằng 0 có nghĩa là không có lệnh
dừng lỗ hoặc chốt lãi được đặt cùng với lệnh. Biến BuyTicket lưu trữ số vé của đơn
hàng. Chúng tôi sử dụng câu lệnh if để kiểm tra xem số BuyTicket có hợp lệ hay không
- tức là lớn hơn 0. Nếu vậy, chúng ta gọi hàm OrderSelect() bằng số BuyTicket của
mình. Chúng tôi truy xuất giá mở cửa cho đơn hàng bằng cách sử dụng
OrderOpenPrice() và gán giá đó cho biến OpenPrice. Tiếp theo, chúng tôi tính toán
mức dừng lỗ và chốt lời tương ứng với giá mở lệnh mà chúng tôi vừa đặt. Trước tiên,
chúng tôi kiểm tra xem các biến bên ngoài StopLoss và TakeProfit có lớn hơn 0 hay
không. Nếu vậy, chúng tôi tính toán mức dừng lỗ và/hoặc giá chốt lời mới. Cuối cùng,
chúng ta gọi hàm OrderModify() để thêm điểm dừng lỗ và chốt lời cho lệnh. Trước tiên,
chúng tôi kiểm tra để đảm bảo rằng các biến BuyStopLoss hoặc BuyTakeProfit khác 0.
Nếu chúng ta cố gắng sửa đổi thứ tự với các giá trị không thay đổi, chúng ta sẽ nhận
được mã lỗi 1 từ hàm OrderModify(). Tham số đầu tiên cho OrderModify() là số
BuyTicket của chúng tôi. Chúng ta cũng có thể sử dụng OrderTicket(). Tham số thứ hai
là giá đặt hàng mới. Vì chúng tôi không sửa đổi giá đặt hàng nên chúng tôi sử dụng
hàm OrderOpenPrice() để chỉ ra rằng giá đặt hàng không thay đổi. Hãy nhớ rằng chúng
tôi chỉ có thể sửa đổi giá đặt lệnh cho các lệnh đang chờ xử lý. Nếu chúng tôi đang
sửa đổi lệnh thị trường, chúng tôi có thể chuyển bất kỳ giá trị nào cho tham số Giá vì
bạn không thể thay đổi giá đặt lệnh của lệnh thị trường. Nhưng chúng ta không thể giả
định rằng chúng ta sẽ luôn sửa đổi các lệnh thị trường, vì vậy chúng ta sẽ luôn sử dụng
OrderOpenPrice(). Các biến BuyStopLoss và BuyTakeProfit chuyển các giá trị dừng lỗ
và chốt lãi đã thay đổi vào hàm OrderModify(). Nếu bạn dự định sử dụng thời gian hết
hạn của đơn hàng cho các đơn hàng đang chờ xử lý của mình, bạn có thể sử dụng
OrderExpiration() làm tham số Hết hạn không thay đổi. Nếu không, chỉ cần sử dụng 0.
Mặc dù phương pháp này bổ sung thêm một số bước nhưng chúng tôi khuyên bạn nên
sử dụng phương pháp đặt mức dừng lỗ và chốt lãi cho các lệnh thị trường trong
chuyên gia cố vấn của mình để đảm bảo rằng chúng tương thích với tất cả các nhà
môi giới. Phương pháp này còn có ưu điểm là cho phép chúng ta đặt mức dừng lỗ và
chốt lời chính xác mà không bị ảnh hưởng bởi trượt giá. 44 www.ZTCprep.com Đặt
lệnh nâng cao Sửa đổi giá lệnh chờ OrderModify() cũng có thể được sử dụng để sửa
đổi giá lệnh của lệnh chờ. Nếu giá lệnh chờ đã được chạm và lệnh đã được khớp thì
lệnh đó không còn là lệnh chờ xử lý nữa và giá không thể thay đổi. Chúng tôi sẽ sử
dụng biến NewPendingprice để biểu thị giá đơn hàng đã thay đổi của chúng tôi. Chúng
tôi sẽ cho rằng giá đã được tính toán và hợp lệ. Đây là cách chúng tôi sửa đổi giá lệnh
chờ: OrderSelect(Ticket,SELECT_BY_TICKET); if(NewPendingPrice !=
OrderOpenPrice()) { bool TicketMod =
OrderModify(Ticket,NewPendingPrice,OrderStopLoss(), OrderTakeProfit(),0); } Như
mọi khi, chúng tôi truy xuất thông tin đơn hàng bằng OrderSelect(). Bằng cách này,
chúng ta có thể chuyển mức dừng lỗ và chốt lời không thay đổi sang hàm
OrderModify(). Trước khi sửa đổi lệnh, chúng tôi sẽ kiểm tra để đảm bảo rằng giá lệnh
chờ mới của chúng tôi không giống với giá lệnh chờ hiện tại. Đối với OrderModify(),
chúng tôi chỉ định phiếu đặt hàng của mình, giá đặt hàng mới được lưu trong
NewPendingPrice và các giá trị dừng lỗ và chốt lời không thay đổi được biểu thị bằng
OrderStopLoss() và OrderTakeProfit(). Chúng tôi không sử dụng thời gian hết hạn cho
đơn hàng này nên chúng tôi sử dụng 0 cho tham số hết hạn. Xác minh điểm dừng và
giá đặt lệnh chờ xử lý Cắt lỗ, chốt lời và giá đặt lệnh chờ phải cách giá Mua và Giá bán
một khoảng tối thiểu. Nếu giá lệnh dừng hoặc lệnh chờ quá gần với giá hiện tại thì sẽ
xảy ra lỗi và lệnh sẽ không được đặt. Đây là một trong những lỗi giao dịch phổ biến
nhất và có thể dễ dàng ngăn chặn nếu người giao dịch cẩn thận đặt điểm dừng và lệnh
chờ ở khoảng cách vừa đủ so với giá. Nhưng trong những thời kỳ giá biến động nhanh,
giá dừng lỗ hợp lệ có thể bị vô hiệu hóa do chênh lệch giá mở rộng. Các nhà môi giới
khác nhau có mức dừng lỗ khác nhau, do đó mức dừng lỗ hợp lệ đối với một nhà môi
giới có thể quá gần đối với một nhà môi giới khác. Một số hệ thống giao dịch sẽ đặt
điểm dừng và giá lệnh chờ dựa trên giá trị chỉ báo, mức cao nhất hoặc mức thấp nhất
hoặc một số phương pháp tính toán khác khi khoảng cách tối thiểu không được đảm
bảo. 45 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Vì những lý do này, luôn
cần phải xác minh rằng mức dừng lỗ, chốt lời hoặc giá đặt lệnh chờ là hợp lệ và không
quá gần với giá thị trường hiện tại. Chúng tôi xác minh điều này bằng cách kiểm tra
mức dừng của tiền tệ. Mức dừng Mức dừng là số pip cách giá Mua hoặc Bán hiện tại
mà tất cả các điểm dừng và lệnh chờ xử lý phải được đặt. Đối với hầu hết các nhà môi
giới, mức dừng lỗ là khoảng 3-4 pip. Các nhà môi giới ECN thường có mức dừng rất
chặt chẽ, trong khi các nhà môi giới khác như Alpari có mức dừng rộng hơn (ít nhất 8
pip). Mức dừng Hình 3.1 minh họa các mức dừng liên quan đến giá. Hãy coi giá không
chỉ là một giá trị duy nhất (chẳng hạn như Giá thầu), mà là một đường dày có chiều
rộng của chênh lệch giá. Hỏi Ở hai bên của đường giá đó là các ranh giới, được biểu thị
bằng điểm dừng Mức chênh lệch. Tất cả các lệnh dừng lỗ, chốt lời và chờ xử lý phải
được đặt bên ngoài các ranh giới này. Giá thầu Hàm MarketInfo() với tham số
MODE_STOPLEVEL là Mức dừng được sử dụng để truy xuất mức dừng cho ký hiệu
tiền tệ. Mức dừng được biểu thị dưới dạng số nguyên và sẽ cần được chuyển đổi
thành Hình 3.1 – Giá trị phân số của mức dừng bằng cách sử dụng Điểm. Đối với loại
tiền có 4 chữ số có mức dừng là 3 pip, hàm MarketInfo() với MODE_STOPLEVEL sẽ trả
về số 3. Đối với loại tiền có 5 chữ số có mức dừng là 3 pip, MarketInfo() sẽ trả về 30,
do có thêm số thập phân địa điểm. Đây là mã để truy xuất mức dừng và chuyển đổi nó
thành giá trị thập phân: double StopLevel = MarketInfo(Symbol(),MODE_STOPLEVEL) *
Point; Lưu ý rằng chúng tôi sử dụng biến Point được xác định trước, thay vì hàm
PipPoint() mà chúng tôi đã tạo trước đó. Điều này là do chúng ta cần nhân mức dừng
lỗ với giá trị điểm thực tế. Đối với loại tiền có 4 chữ số, Điểm sẽ là 0,0001 và đối với
loại tiền có 5 chữ số, Điểm sẽ là 0,00001. Nếu mức dừng là 3 pip như minh họa ở trên
thì giá trị phân số sẽ là 0,0003. Bây giờ chúng ta đã tìm ra cách tìm mức dừng, chúng
ta cần tính giá trị tối thiểu và tối đa cho mức dừng lỗ, chốt lãi và giá đặt lệnh chờ.
Chúng tôi thực hiện việc này bằng cách cộng hoặc trừ mức dừng khỏi giá Mua và Bán
hiện tại của chúng tôi. 46 www.ZTCprep.com Đặt lệnh nâng cao Mã này sẽ tính toán
mức giá tối thiểu được phép cho lệnh mua chốt lời, lệnh dừng lỗ bán, lệnh dừng mua
hoặc lệnh giới hạn bán. Chúng tôi sẽ sử dụng giá trị StopLevel mà chúng tôi đã tính ở
trên. gấp đôi UpperStopLevel = Hỏi + StopLevel; Nếu giá Hỏi của chúng tôi là 1,4650
và Mức dừng là 0,0003 pip như đã tính ở trên thì giá mức dừng tối thiểu sẽ là 1,4653.
Nếu chúng ta đặt lệnh mua chốt lời với lệnh này thì lệnh này phải cao hơn mức giá này.
Chúng tôi sẽ gọi đây là UpperStopLevel vì nó cao hơn giá. Mã này sẽ tính toán mức giá
tối đa được phép cho lệnh bán chốt lời, lệnh dừng lỗ mua, lệnh dừng bán hoặc lệnh
giới hạn bán. Lưu ý rằng chúng tôi chỉ sử dụng Giá thầu thay vì Hỏi và trừ thay vì thêm.
gấp đôi LowerStopLevel = Giá thầu - StopLevel; Chúng tôi sẽ gọi đây là
LowerStopLevel vì nó thấp hơn giá. Trước khi đặt lệnh, hãy sử dụng các giá trị
UpperStopLevel và LowerStopLevel ở trên để xác minh mức dừng lỗ, chốt lời và giá
đặt lệnh đang chờ xử lý của bạn. Hãy nhớ rằng giá có thể thay đổi nhanh chóng và bạn
sẽ muốn các điểm dừng, lợi nhuận và lệnh chờ thực tế của mình nằm ngoài các mức
này. Xác minh giá dừng lỗ và chốt lời Mức chốt lời tối thiểu tính bằng pip sẽ bằng giá
mở lệnh, cộng hoặc trừ mức dừng. Nếu mức dừng là 3 pip và giá mở lệnh là 1,4500 thì
giá chốt lời cho lệnh mua sẽ cần phải cao hơn 1,4503. Tuy nhiên, mức dừng lỗ tối thiểu
tính bằng pip cho một lệnh thị trường sẽ bao gồm mức chênh lệch hiện tại, do đó mức
dừng lỗ tối thiểu sẽ lớn hơn mức chốt lời tối thiểu. Ví dụ: nếu mức dừng là 3 pip, chênh
lệch là 2 pip và giá mở lệnh là 1,4500 thì mức dừng lỗ cho lệnh thị trường mua sẽ cần
phải dưới 1,4495. Điều này không áp dụng cho các lệnh đang chờ xử lý, vì vậy khi xác
minh mức dừng lỗ cho một lệnh đang chờ xử lý, không cần thiết phải tính đến mức
chênh lệch. Vì vậy, nếu bạn đang đặt một lệnh chờ ở mức 1,4500 và mức dừng lỗ là 3
pip thì mức dừng lỗ có thể được đặt ở bất kỳ đâu dưới 1,4497. Đây là một ví dụ trong
đó chúng tôi kiểm tra mức dừng lỗ và chốt lời cho lệnh mua để đảm bảo giá cả hợp lệ.
Nếu giá dừng lỗ hoặc chốt lãi không hợp lệ, chúng tôi sẽ tự động điều chỉnh nó sao
cho nó nằm ngoài mức dừng vài pip. 47 www.ZTCprep.com CHUYÊN GIA CỐ VẤN LẬP
TRÌNH đôi MinStop = 5 * UsePoint; if(BuyStopLoss > LowerStopLevel) BuyStopLoss =
LowerStopLevel - MinStop; if(BuyTakeProfit < UpperStopLevel) BuyTakeProfit =
UpperStopLevel + MinStop; Biến MinStop cộng hoặc trừ 5 pip so với mức dừng, để
đảm bảo rằng giá được xác thực của chúng tôi không trở nên vô hiệu do trượt giá. Bạn
có thể điều chỉnh giá trị này để thực thi mức dừng/lợi nhuận tối thiểu đủ hoặc thậm
chí sử dụng biến bên ngoài để điều chỉnh số tiền này. Dòng thứ hai so sánh điểm dừng
lỗ của chúng tôi với LowerStopLevel. Nếu mức dừng lỗ lớn hơn mức dừng lỗ thấp hơn
của chúng tôi, chúng tôi biết rằng lệnh dừng lỗ không hợp lệ. Trong trường hợp đó,
chúng tôi điều chỉnh mức dừng lỗ chỉ thấp hơn mức dừng lỗ của chúng tôi vài pip.
Dòng thứ ba thực hiện tương tự đối với lệnh chốt lời của chúng ta. Để kiểm tra mức
dừng lỗ và chốt lãi cho một lệnh bán, chúng ta chỉ cần đảo ngược các phép tính:
if(SellTakeProfit > LowerStopLevel) SellTakeProfit = LowerStopLevel - MinStop;
if(SellStopLoss < UpperStopLevel) SellStopLoss = UpperStopLevel + MinStop; Thay vì
tự động điều chỉnh giá không hợp lệ, bạn cũng có thể hiển thị thông báo lỗi và tạm
dừng thực hiện chương trình. Bằng cách này, người dùng sẽ được yêu cầu điều chỉnh
lại cài đặt dừng lỗ hoặc chốt lời trước khi tiếp tục. Sau đây là ví dụ về cách thực hiện
việc này: if(BuyStopLoss > LowerStopLevel) { Alert("Cài đặt dừng lỗ quá nhỏ!"); trở lại
(0); } Nếu mức dừng lỗ được tính toán cao hơn mức dừng và do đó quá gần với giá,
hàm Alert() sẽ hiển thị thông báo bật lên cho người dùng. Toán tử trả về thoát khỏi
hàm hiện tại và đảm bảo rằng đơn hàng sẽ không được đặt. Trong cuốn sách này,
chúng tôi sẽ tự động điều chỉnh các mức giá không hợp lệ, với giả định rằng đặt một
lệnh đã sửa sẽ tốt hơn là không đặt một lệnh nào cả. Có thể hữu ích khi ghi lại khi điều
này xảy ra bằng cách in thông báo vào nhật ký: if(BuyStopLoss > LowerStopLevel) {
BuyStopLoss = LowerStopLevel - MinStop; Print("Dừng lỗ không hợp lệ và đã được
điều chỉnh tự động"); } 48 www.ZTCprep.com Xác minh vị trí đặt lệnh nâng cao Xác
minh giá đặt hàng đang chờ xử lý Đây là cách chúng tôi xác minh giá đặt hàng đang
chờ xử lý cho lệnh dừng mua hoặc lệnh giới hạn bán. Biến PendingPrice lưu trữ giá
lệnh chờ xử lý của chúng tôi: if(PendingPrice < UpperStopLevel) PendingPrice =
UpperStopLevel + MinStop; Lưu ý rằng logic ở đây giống hệt với đoạn mã ở trên để
kiểm tra giá mua chốt lãi và giá dừng lỗ bán của chúng tôi. Và đây là mã để kiểm tra
giá lệnh chờ đối với lệnh dừng bán hoặc lệnh giới hạn mua: if(PendingPrice >
UpperStopLevel) PendingPrice = UpperStopLevel – MinStop; Tính toán kích thước lô
Ngoài việc chọn mức dừng lỗ và chốt lời phù hợp, sử dụng kích thước lô phù hợp là
một trong những công cụ quản lý rủi ro tốt nhất mà bạn có. Việc chỉ định kích thước lô
có thể đơn giản như khai báo một biến bên ngoài và sử dụng kích thước lô cố định cho
mỗi đơn hàng. Trong phần này, chúng ta sẽ khám phá một phương pháp phức tạp hơn
để tính toán kích thước lô dựa trên số tiền tối đa bạn sẵn sàng thua cho mỗi giao dịch.
Đòn bẩy quá mức là một trong những kẻ giết người lớn đối với các nhà giao dịch ngoại
hối. Việc sử dụng kích thước lô quá lớn so với vốn sở hữu của bạn có thể xóa sạch tài
khoản của bạn một cách dễ dàng cũng như có thể tạo ra lợi nhuận lớn. Bạn nên sử
dụng không quá 2-3% vốn sở hữu của mình cho mỗi giao dịch. Bằng cách này, chúng
tôi muốn nói rằng số tiền tối đa bạn có thể mất cho mỗi giao dịch sẽ không quá 2-3%
tài khoản của bạn. Quản lý tiền Để tính toán kích thước lô bằng phương pháp này,
chúng ta cần chỉ định tỷ lệ phần trăm vốn sở hữu sẽ sử dụng và mức dừng lỗ tính bằng
pip. Chúng tôi sẽ sử dụng biến bên ngoài EquityPercent để đặt tỷ lệ phần trăm vốn chủ
sở hữu sẽ sử dụng. Chúng tôi giả định mức dừng lỗ là 50 pip được sử dụng. bên ngoài
gấp đôi EquityPercent = 2; StopLoss kép bên ngoài = 50; Đầu tiên, chúng ta cần tính
lượng vốn chủ sở hữu được chỉ định bởi EquityPercent. Nếu chúng tôi có số dư 10.000
USD và chúng tôi đang sử dụng 2% vốn sở hữu của mình thì cách tính như sau: double
RiskAmount = AccountEquity() * (EquityPercent / 100); 49 www.ZTCprep.com LẬP
TRÌNH TƯ VẤN CHUYÊN GIA AccountEquity() là một hàm MQL trả về vốn chủ sở hữu
của tài khoản hiện tại. Chúng tôi chia EquityPercent cho 100 để có giá trị phân số
(0,02). Sau đó, chúng tôi nhân số đó với AccountEquity() để tính lượng vốn sở hữu cần
sử dụng. 2% của 10.000 USD là 200 USD và số tiền này sẽ được lưu trữ trong biến
RiskAmount. Tiếp theo, chúng ta phải tìm giá trị đánh dấu. Đây là lợi nhuận trên mỗi
pip nếu chúng ta giao dịch một lượng lớn loại tiền tệ mong muốn. Ví dụ: nếu chúng ta
đang giao dịch 1 lô EURUSD trên tài khoản tiêu chuẩn (lô 100 nghìn), lợi nhuận trên
mỗi pip sẽ là 10 USD. Trên tài khoản mini (lô 10 nghìn), lợi nhuận trên mỗi pip sẽ là 1
đô la. Chúng ta có thể sử dụng hàm MarketInfo() với tham số MODE_TICKVALUE để
trả về lợi nhuận trên mỗi pip cho loại tiền được chỉ định. Giá trị đánh dấu phải tính
bằng pip, vì vậy nếu chúng ta đang giao dịch trên một nhà môi giới pip phân số (3
hoặc 5 chữ số thập phân), chúng ta phải nhân giá trị đánh dấu với 10. double
TickValue = MarketInfo(Symbol(),MODE_TICKVALUE); if(Điểm == 0,001 || Điểm ==
0,00001) TickValue *= 10; Giả sử chúng ta đang giao dịch trên một tài khoản tiêu
chuẩn, giá trị đánh dấu cho EURUSD sẽ là 10. Giá trị này sẽ được lưu trữ trong biến
TickValue. Nếu đây là một nhà môi giới pip phân số thì TickValue sẽ là 1. Chúng ta sẽ
cần nhân số này với 10 để nó tương đương với một pip. Nếu biến Điểm chỉ ra rằng loại
tiền tệ có 3 hoặc 5 chữ số thập phân thì TickValue sẽ được nhân với 10 để bằng giá trị
2 hoặc 4 chữ số thập phân. Bước tiếp theo là tính toán kích thước lô của chúng tôi.
Đầu tiên, chúng tôi chia Số tiền rủi ro cho cài đặt Dừng lỗ. Điều này sẽ mang lại cho
chúng tôi lợi nhuận trên mỗi tích tắc cho đơn hàng này. 200 USD chia cho mức dừng lỗ
50 sẽ cho chúng ta 4 USD. Bây giờ tất cả những gì chúng ta phải làm là chia số đó cho
TickValue để có kích thước lô: double CalcLots = (RiskAmount / StopLoss) /
TickValue; Kích thước lô được tính toán của chúng tôi trên tài khoản tiêu chuẩn sẽ là
0,4 lô. Trên tài khoản mini, kích thước lô được tính sẽ là 4 lô. Giá trị này được lưu trữ
trong biến CalcLots. Nếu bạn đang sử dụng cách quản lý tiền phù hợp, tỷ lệ vốn chủ sở
hữu bạn đang sử dụng sẽ khá nhất quán. (1-2% đối với rủi ro thận trọng, lên tới 5% đối
với rủi ro cao hơn). Mặt khác, mức dừng lỗ của bạn sẽ thay đổi tùy theo khung thời
gian và hệ thống giao dịch của bạn. Kích thước lô sẽ rất khác nhau tùy thuộc vào điểm
dừng lỗ của bạn. Lệnh dừng lỗ chặt chẽ sẽ tạo ra kích thước lô lớn hơn, mang lại nhiều
lợi ích tăng giá nếu lệnh của bạn đạt mức chốt lời. Mặt khác, nếu bạn đang sử dụng
mức dừng lỗ lớn, kích thước lô của bạn sẽ khá nhỏ. Phương pháp này sẽ được hưởng
lợi tốt nhất từ ​việc sử dụng các điểm dừng khá chặt chẽ và/hoặc giá trị chốt lời lớn. 50
www.ZTCprep.com Đặt lệnh nâng cao Nếu bạn phải sử dụng mức dừng lỗ lớn hoặc
không sử dụng mức dừng lỗ nào, kích thước lô cố định có thể sẽ có lợi hơn. Chúng ta
cần có khả năng lựa chọn giữa việc tính toán kích thước lô hoặc sử dụng kích thước lô
cố định. Hãy sử dụng một biến boolean bên ngoài có tên DynamicLotSize để bật và tắt
tính toán kích thước lô của chúng ta: // Biến bê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; // Bắt
đầu hàm if(DynamicLotSize == true) { double RiskAmount = AccountEquity() *
(EquityPercent / 100); double TickValue = MarketInfo(Symbol(),MODE_TICKVALUE);
if(Chữ số == 3 || Chữ số == 5) TickValue *= 10; gấp đôi CalcLots = (RiskAmount /
StopLoss) / TickValue; gấp đôi Kích thước lô = CalcLots; } else LotSize = FixLotSize;
Nếu DynamicLotSize được đặt thành true, chúng tôi sẽ tính toán kích thước lô dựa trên
mức dừng lỗ và gán giá trị đó cho biến LotSize. Nếu DynamicLotSize sai, chúng tôi chỉ
cần gán giá trị của FixLotSize cho LotSize. Biến LotSize sẽ được chuyển đến hàm
OrderSend() dưới dạng kích thước lô cho đơn hàng. Xác minh kích thước lô Cũng
giống như mức dừng lỗ, chốt lời và giá đặt lệnh chờ xử lý, kích thước lô cũng phải
được xác minh để đảm bảo nó được nhà môi giới của bạn chấp nhận. Điều này có
nghĩa là kích thước lô của bạn không được quá lớn hoặc quá nhỏ và không được chỉ
định kích thước lô siêu nhỏ (0,01) nếu nhà môi giới của bạn không hỗ trợ những lô đó.
Bạn cũng nên chuẩn hóa kích thước lô của mình thành vị trí thập phân thích hợp.
Trước tiên hãy kiểm tra kích thước lô tối thiểu và tối đa. Hàm MarketInfo(), sử dụng
tham số MODE_MINLOT và MODE_MAXLOT, sẽ được sử dụng để so sánh kích thước
lô hiện tại với kích thước lô tối thiểu và tối đa. Nếu kích thước lô không hợp lệ, nó sẽ tự
động được thay đổi kích thước ở mức tối thiểu hoặc tối đa. if(LotSize <
MarketInfo(Symbol(),MODE_MINLOT)) { LotSize =
MarketInfo(Symbol(),MODE_MINLOT); } 51 www.ZTCprep.com CHUYÊN GIA LẬP
TRÌNH TƯ VẤN else if(LotSize > MarketInfo(Symbol(),MODE_MAXLOT)) { LotSize =
MarketInfo(Symbol(),MODE_MAXLOT); } Chúng tôi chỉ cần so sánh giá trị của LotSize,
kích thước lô được tính toán hoặc cố định của chúng tôi ở trên, với kích thước lô tối
thiểu và tối đa. Nếu LotSize nhỏ hơn kích thước lô tối thiểu hoặc lớn hơn kích thước lô
tối đa thì nó sẽ được chỉ định giá trị tối thiểu hoặc tối đa thích hợp. Tiếp theo, chúng ta
cần so sánh kích thước lô của mình với giá trị bước. Giá trị bước cho biết nhà môi giới
cho phép lô siêu nhỏ (0,01) hay lô nhỏ (0,1). Nếu bạn cố gắng sử dụng kích thước lô
siêu nhỏ trên một nhà môi giới chỉ cho phép lô nhỏ, bạn sẽ gặp lỗi và giao dịch sẽ
không được thực hiện. Đây là mã để kiểm tra giá trị bước:
if(MarketInfo(Symbol(),MODE_LOTSTEP) == 0.1) { LotSize =
NormalizeDouble(LotSize,1); } else LotSize = NormalizeDouble(LotSize,2); Hàm
NormalizeDouble() làm tròn giá trị của LotSize thành số chữ số được chỉ định trong
đối số thứ hai. Ở dòng đầu tiên, nếu kích thước bước là 0,1, cho biết nhà môi giới chỉ
sử dụng lô nhỏ, LotSize sẽ được làm tròn đến một chữ số thập phân. Ngược lại
LotSize sẽ được làm tròn đến 2 chữ số thập phân. Nếu trong tương lai bạn tình cờ gặp
một nhà môi giới cho phép kích thước lô lên tới ba chữ số thập phân thì bạn cũng có
thể dễ dàng sửa đổi mã ở trên để kiểm tra điều đó. Nhưng hiện tại, hầu như mọi nhà
môi giới MetaTrader đều sử dụng một hoặc hai chữ số thập phân để định cỡ lô. Những
cân nhắc khác Bối cảnh giao dịch MetaTrader có một chuỗi thực hiện giao dịch duy
nhất dành cho các cố vấn chuyên gia. Điều này có nghĩa là tại một thời điểm chỉ có
một chuyên gia cố vấn có thể giao dịch, bất kể có bao nhiêu chuyên gia cố vấn đang
chạy trong thiết bị đầu cuối. Trước khi bắt đầu với bất kỳ hoạt động giao dịch nào,
chúng ta phải kiểm tra xem liệu chuỗi thực hiện giao dịch hiện có đang được sử dụng
hay không. Hàm IsTradeContextBusy() sẽ trả về true nếu luồng thực hiện giao dịch bị
chiếm dụng, nếu không thì trả về false. Chúng ta sẽ gọi hàm này ngay trước khi gọi bất
kỳ hàm giao dịch nào, bao gồm OrderSend(), OrderClose(), OrderDelete() hoặc
OrderModify(). 52 www.ZTCprep.com Đặt lệnh nâng cao Đây là cách chúng tôi kiểm
tra chuỗi thực hiện giao dịch bằng cách sử dụng IsTradeContextBusy():
while(IsTradeContextBusy()) Sleep(10); int Ticket =
OrderSend(Symbol(),OP_BUY,LotSize,Ask,UseSlippage,0,0, "Buy
Order",MagicNumber,0,Green); Chúng tôi sử dụng vòng lặp while để đánh giá
IsTradeContextBusy(). Nếu hàm trả về đúng, cho biết luồng thực hiện giao dịch đã bị
chiếm dụng, chuyên gia tư vấn sẽ Ngủ trong 10 mili giây. Vòng lặp while sẽ tiếp tục
thực thi miễn là IsTradeContextBusy() trả về true. Khi chuỗi giao dịch được giải phóng,
giao dịch sẽ bắt đầu. Nếu cố vấn chuyên gia cố gắng giao dịch trong khi chuỗi thực
hiện giao dịch bị bận thì sẽ xảy ra lỗi 147: "bối cảnh giao dịch bận". Mặc dù phương
pháp này khá đáng tin cậy trong việc tránh lỗi "bối cảnh giao dịch bận", nhưng nó
không an toàn, đặc biệt khi nhiều cố vấn chuyên gia đang cố gắng giao dịch cùng một
lúc. Ở phần sau của cuốn sách, chúng ta sẽ khám phá các cách để thử lại các hoạt
động giao dịch sau một số điều kiện lỗi nhất định. Làm mới các biến được xác định
trước Các giá trị của các biến được xác định trước như Giá thầu và Hỏi được đặt khi
cố vấn chuyên gia bắt đầu thực hiện. Lượng thời gian cần thiết để thực thi mã Expert
Advisor của chúng tôi rất ngắn và có thể được tính bằng mili giây. Nhưng khi bạn nhận
thấy có sự chậm trễ trong phản hồi của máy chủ giao dịch và thực tế là giá có thể thay
đổi rất nhanh, điều quan trọng là bạn phải luôn sử dụng mức giá mới nhất. Hàm
RefreshRates() cập nhật nội dung của các biến được xác định trước với giá mới nhất
từ ​máy chủ. Bạn nên gọi hàm này mỗi khi sử dụng biến Giá thầu hoặc Bán, đặc biệt là
sau khi thực hiện giao dịch trước đó. Lưu ý rằng nếu bạn truy xuất giá bằng hàm
MarketInfo() thì không cần thiết phải sử dụng RefreshRates(). Chúng ta đã đề cập đến
MarketInfo() ở trang 29. Khi chuyển sang chương tạo hàm, chúng ta sẽ sử dụng
MarketInfo() để truy xuất giá thay vì sử dụng các biến được xác định trước. Tuy nhiên,
bạn vẫn có thể muốn sử dụng Giá thầu và Hỏi trong hàm start() của mình để tham
khảo giá biểu đồ hiện tại. Xử lý lỗi Khi đặt, sửa đổi hoặc đóng lệnh, lỗi có thể xảy ra do
thông số giao dịch không hợp lệ, báo giá lại hoặc sự cố máy chủ. Chúng tôi đã cố
gắng hết sức để đảm bảo rằng các tham số giao dịch mà chúng tôi sử dụng là hợp lệ
và đã được kiểm tra để ngăn ngừa các lỗi phổ biến có thể phòng ngừa được. Nhưng
khi xảy ra lỗi, chúng tôi cần thông báo cho người dùng về lỗi đó và ghi lại mọi thông tin
liên quan để khắc phục sự cố. 53 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN
GIA Chúng tôi kiểm tra các lỗi có thể xảy ra bằng cách kiểm tra đầu ra của các hàm
như OrderSend(), OrderModify() và OrderClose(). Nếu hàm không hoàn thành thành
công, hàm sẽ trả về -1 cho OrderSend() hoặc false cho OrderModify() và OrderClose().
Trong phần này, chúng ta sẽ tạo một quy trình xử lý lỗi cho hàm OrderSend(). Nếu giá
trị trả về của OrderSend() là -1, chúng tôi sẽ chạy quy trình xử lý lỗi để hiển thị cảnh
báo cho người dùng và in thông số giao dịch và thông tin giá có liên quan vào nhật ký.
Đầu tiên, trước tiên chúng ta phải lấy mã lỗi. Việc này được thực hiện bằng hàm
GetLastError(). Chúng ta cần lưu trữ giá trị trả về của GetLastError() trong một biến, vì
khi GetLastError() được gọi, mã lỗi sẽ bị xóa và lệnh gọi tiếp theo của GetLastError()
sẽ trả về 0. Chúng ta sẽ khai báo một biến toàn cục có tên là ErrorCode và sử dụng nó
để lưu trữ giá trị của GetLastError(). Tiếp theo, chúng ta cần lấy một số thông tin mô tả
về lỗi. Tệp bao gồm stdlib.mqh chứa hàm có tên ErrorDescription(). Hàm này trả về
một chuỗi mô tả lỗi. Thực ra nó không mang tính mô tả nhiều lắm, nhưng có còn hơn
không. Chúng ta sẽ cần thêm câu lệnh #include cho stdlib.mqh ở đầu tệp của mình.
Sau đó, chúng tôi sẽ in cảnh báo ra màn hình của người dùng bằng hàm Alert() tích
hợp sẵn. Thông tin này cũng sẽ được in vào nhật ký. Cảnh báo sẽ bao gồm mã lỗi, mô
tả lỗi và mô tả ngắn gọn về thao tác mà chúng tôi vừa cố gắng thực hiện. Bằng cách
này, bạn sẽ biết chính xác phần nào trong chương trình của bạn đã tạo ra lỗi. Cuối
cùng, chúng ta sẽ in thông tin giá liên quan vào nhật ký bằng hàm Print(). Cùng với giá
Mua và Bán hiện tại, chúng tôi sẽ bao gồm các thông số giao dịch như kích thước lô và
giá đặt hàng. // Phần tiền xử lý #include // Biến toàn cục int ErrorCode; // Đặt lệnh int
Ticket = OrderSend(Symbol(),OP_BUYSTOP,LotSize,PendingPrice,UseSlippage,0,0, "Buy
Stop Order",MagicNumber,0,Green); if(Ticket == -1) { 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); 54 www.ZTCprep.com
Chuỗi đặt lệnh nâng cao ErrLog = StringConcatenate("Bid: ",Bid," Ask: ",Ask," Price: ",
PendingPrice," Lots: ",LotSize); In(ErrLog); } Ở trên cùng, chúng tôi bao gồm tệp
stdlib.mqh. Chúng tôi thêm biến toàn cục ErrorCode để lưu trữ mã lỗi của mình.
OrderSend() đặt lệnh dừng mua. Nếu chức năng không thành công, mã xử lý lỗi của
chúng tôi sẽ chạy. Đầu tiên, chúng ta lưu trữ giá trị của GetLastError() trong ErrorCode.
Sau đó, chúng ta gọi hàm ErrorDescription(), sử dụng ErrorCode làm đối số. Tiếp theo,
chúng ta sử dụng hàm StringConcatenate() để tạo thông báo cảnh báo, thông báo này
được lưu trữ trong biến chuỗi ErrAlert. StringConcatenate() là một hàm MQL cho phép
bạn tạo các chuỗi phức tạp bằng cách sử dụng các biến và hằng. Mỗi phần tử chuỗi
được nối (hoặc "nối") với nhau được phân tách bằng dấu phẩy. Hãy thử nhập các ví dụ
trên vào MetaEditor để xem nó bằng cách tô sáng cú pháp. Bạn cũng có thể nối các
chuỗi bằng cách kết hợp chúng bằng dấu cộng (+). Sử dụng StringConcatenate() rõ
ràng và hiệu quả hơn, nhưng nếu bạn chỉ muốn nối một chuỗi ngắn, hãy sử dụng dấu
cộng để kết hợp các hằng và biến chuỗi: string PlusCat = "Giá bán hiện tại là "+Ask; //
Đầu ra mẫu: Giá Ask hiện tại là 1.4320 Hàm Alert() hiển thị một cửa sổ bật lên trên
màn hình của người dùng, chứa nội dung của biến ErrAlert. Hình 3.2 hiển thị đầu ra
của hàm Alert(). Chúng tôi xây dựng một chuỗi khác với các tham số giá và giao dịch
của mình, đồng thời lưu trữ nó trong biến ErrLog mà chúng tôi chuyển đến hàm Print().
Print() in nội dung của đối số hàm vào nhật ký chuyên gia. Bạn có thể xem nhật ký
chuyên gia từ tab Chuyên gia bên trong cửa sổ Terminal hoặc từ tab Nhật ký trong cửa
sổ Trình kiểm tra nếu bạn đang sử dụng Trình kiểm tra chiến lược. 55
www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Hình 3.2 – Thông báo cảnh báo
Dưới đây là nội dung nhật ký. Dòng đầu tiên là kết quả của hàm Alert(). Dòng thứ hai là
đầu ra của hàm Print(). Lưu ý lỗi "khối lượng giao dịch không hợp lệ" và thực tế là kích
thước lô được báo cáo trong nhật ký là 0. Trong trường hợp này, vấn đề là kích thước
lô không hợp lệ. 16:47:54 Profit Buster EURUSD,H1: Cảnh báo: Mở Lệnh dừng mua -
Lỗi 131: khối lượng giao dịch không hợp lệ 16:47:54 Profit Buster EURUSD,H1: Giá
thầu: 1,5046, Hỏi: 1,5048, Lô: 0 Bạn có thể tạo tương tự các quy trình xử lý lỗi cho các
hàm khác, đặc biệt là các hàm OrderModify() và OrderClose(). Bạn cũng có thể tạo
các quy trình xử lý lỗi phức tạp hơn để cung cấp thông báo lỗi tùy chỉnh dựa trên mã
lỗi hoặc thực hiện các hành động khác. Ví dụ: nếu bạn nhận được lỗi 130: "điểm dừng
không hợp lệ", bạn có thể hiển thị thông báo như "Giá dừng lỗ hoặc chốt lời không hợp
lệ". Đây là ví dụ về cách bạn có thể thực hiện việc này: ErrorCode = GetLastError();
chuỗi ErrDesc; if(ErrorCode == 129) ErrDesc = "Giá mở lệnh không hợp lệ!";
if(ErrorCode == 130) ErrDesc = "Dừng lỗ hoặc chốt lãi không hợp lệ!"; if(ErrorCode ==
131) ErrDesc = "Kích thước lô không hợp lệ!"; string ErrAlert = StringConcatenate("Mở
lệnh mua - Lỗi", ErrorCode,": ",ErrDesc); Cảnh báo(ErrAlert); 56 www.ZTCprep.com Đặt
hàng nâng cao Kết hợp tất cả lại với nhau Chúng tôi sẽ thêm tất cả các tính năng mà
chúng tôi đã đề cập trong phần này vào chuyên gia cố vấn đơn giản mà chúng tôi đã
tạo ở trang 36. Chúng tôi sẽ thêm sửa đổi đơn hàng, dừng lại xác minh cấp độ, kiểm
tra bối cảnh giao dịch, làm mới biến được xác định trước và xác minh kích thước lô
cho EA của chúng tôi. Đây là tệp của chúng tôi, bắt đầu từ đầu: #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;
Chúng tôi đã thêm câu lệnh #include cho tệp stdlib.mqh có chứa hàm
ErrorDescription() cho quy trình xử lý lỗi của chúng tôi. Chúng tôi đã thêm ba biến bên
ngoài để xác định kích thước lô và một biến chung cho mã lỗi. Đoạn mã sau đặt ở đầu
hàm 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); 57 www.ZTCprep.com LẬP TRÌNH CỐ VẤN
CHUYÊN GIA // 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); Mã xác minh và tính toán kích thước lô từ trang 51 được
thêm vào đầu hàm bắt đầu của chúng tôi. Vì mức dừng lỗ của chúng ta đã được biết
trước nên đây là một nơi tốt để đặt nó. Đoạn mã còn lại là quy trình đặt hàng thị
trường mua đã được sửa đổi của chúng tôi: // Đặt hàng mua if(FastMA > SlowMA &&
BuyTicket == 0) { // Đóng đơn hàng 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); 58
www.ZTCprep.com Vị trí đặt hàng nâng cao // 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); } } // 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; 59 www.ZTCprep.com CHƯƠNG TRÌNH CỐ VẤN
CHUYÊN GIA // Tính toán mức dừng lỗ và chốt lời nếu (StopLoss > 0) gấp đôi
BuyStopLoss = OpenPrice - (StopLoss * UsePoint); if(TakeProfit > 0) double
BuyTakeProfit = OpenPrice + (TakeProfit * UsePoint); // 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 lệnh mua - 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; } Phần còn lại của mã của chúng tôi chứa khối vị trí đặt lệnh thị trường
bán cũng như các hàm PipPoint() và GetSlippage(). Bạn có thể xem mã đầy đủ cho
chuyên gia cố vấn này trong Phụ lục B. Lưu ý rằng chúng tôi đã thêm hàm
IsTradeContextBusy() trước mỗi hoạt động giao dịch. Chúng tôi muốn đảm bảo rằng
chuỗi giao dịch được tự do trước khi thử giao dịch. Chúng tôi sử dụng chức năng Đặt
hàng nâng cao RefreshRates() 60 www.ZTCprep.com trước mỗi tham chiếu của các
biến Giá thầu hoặc Hỏi để đảm bảo rằng chúng tôi luôn sử dụng giá mới nhất. Chúng
ta bắt đầu bằng cách chọn vé lệnh bán trước đó và đóng nó bằng OrderClose(). Nếu
chức năng không thành công, khối xử lý lỗi sẽ được chạy. Tiếp theo, chúng ta mở lệnh
thị trường mua bằng OrderSend(). Nếu chức năng không thành công, khối xử lý lỗi sẽ
được chạy. Nếu không, chúng ta tiếp tục khối sửa đổi thứ tự. Chúng tôi chọn đơn hàng
vừa được đặt bằng OrderSelect() và gán giá mở đơn hàng cho biến OpenPrice. Sau đó,
chúng tôi tính toán mức dừng và giá mức dừng trên và dưới. Tiếp theo, chúng tôi tính
toán mức dừng lỗ và chốt lời, xác minh các giá đó và cuối cùng chúng tôi sửa đổi lệnh
bằng OrderModify(). Khối xử lý lỗi cuối cùng xử lý các lỗi từ việc sửa đổi đơn hàng.
Đây là cách chúng tôi sửa đổi mã cho lệnh dừng mua đang chờ xử lý: // Đó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); } } 61 www.ZTCprep.com LẬP TRÌNH
TƯ VẤN CHUYÊN GIA // 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); } } Chúng tôi đã thêm mã để xóa các lệnh đang chờ xử lý
bằng cách sử dụng OrderDelete() sau hàm OrderClose(). Loại lệnh của lệnh bán trước
đó xác định chức năng nào được sử dụng để đóng lệnh. Sự khác biệt chính giữa mã
sau đây và mã lệnh thị trường là chúng ta không có khối sửa đổi lệnh. Không cần thiết
phải đặt lệnh dừng lỗ và chốt lời riêng cho các lệnh chờ. Do đó chúng ta sẽ tính toán
mức dừng lỗ và chốt lời trước khi đặt lệnh bằng OrderSend(). // Tính 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; 62 www.ZTCprep.com Đặt lệnh
nâng cao 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; Đầu tiên, chúng tôi tính toán mức dừng trên. Sau đó, chúng tôi tính toán
và xác minh giá đặt hàng đang chờ xử lý, được lưu trữ trong PendingPrice. Sau đó,
chúng tôi tính toán lại UpperStopLevel và tính LowerStopLevel sao cho chúng có liên
quan đến giá lệnh chờ xử lý. Lưu ý rằng chúng ta không cần sử dụng giá Hỏi hoặc Giá
thầu hoặc tính toán mức chênh lệch khi xác minh mức dừng lỗ và chốt lãi. Cuối cùng,
chúng tôi đặt lệnh chờ xử lý bằng cách sử dụng OrderSend(), đặt mức dừng lỗ và chốt
lời cùng với nó. Chúng tôi có chức năng xử lý lỗi tiêu chuẩn để xử lý các lỗi đặt hàng.
Bất chấp tất cả các mã bổ sung, các cố vấn chuyên gia này đang sử dụng chiến lược
tương tự như chiến lược ở cuối chương 2. Mã này chỉ đơn giản có các tính năng bổ
sung để tính toán và xác minh kích thước lô, mức dừng, dừng lỗ, chốt lãi và giá lệnh
chờ. Chúng tôi cũng đã thêm mã kiểm tra bối cảnh thương mại và xử lý lỗi. Trong
chương tiếp theo, chúng ta sẽ tìm hiểu cách tạo các hàm để có thể sử dụng lại và đơn
giản hóa mã này. 63 www.ZTCprep.com CHƯƠNG TRÌNH CỐ VẤN CHUYÊN GIA
Chương 4 Làm việc với các hàm Chúng ta sẽ chuyển mã mà chúng ta đã thảo luận
trong các chương trước thành các hàm có thể sử dụng lại. Điều này sẽ giúp chúng ta
tiết kiệm rất nhiều công sức vì chúng ta có thể tập trung vào các chi tiết của hệ thống
giao dịch thay vì cơ chế giao dịch. Ý tưởng đằng sau việc tạo các hàm là tạo một khối
mã thực hiện một nhiệm vụ rất cụ thể. Mã phải đủ linh hoạt để được sử dụng lại trong
nhiều tình huống giao dịch khác nhau. Bất kỳ biến hoặc phép tính bên ngoài nào cũng
sẽ cần được chuyển đến hàm. Nếu không, chúng ta không thể giả định rằng bất kỳ giá
trị cần thiết nào sẽ có sẵn cho hàm của chúng ta, vì hàm này có thể nằm trong tệp
hoặc thư viện bao gồm bên ngoài. Để nhất quán, chúng tôi sẽ giữ nguyên tên cho mọi
biến bên ngoài mà chúng tôi đã sử dụng cho đến nay. Chúng ta sẽ mở đầu các biến
này bằng "arg", để chỉ ra rằng chúng là các đối số của hàm. Hàm định cỡ lô Hãy bắt
đầu với phép tính kích thước lô, như được xác định trên trang 51: double
CalcLotSize(bool argDynamicLotSize, double argEquityPercent, double argStopLoss,
double argFixedLotSize) { if(argDynamicLotSize == true) { 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); } Dòng đầu tiên là phần khai báo hàm của
chúng ta. Chúng tôi gọi hàm này là CalcLotSize(). So sánh mã này với mã ở trang 51.
Lưu ý rằng DynamicLotSize, EquityPercent, StopLoss và FixLotSize hiện đều là các đối
số của hàm. Các biến bên ngoài có tên này vẫn tồn tại trong chương trình của chúng
ta, bây giờ chúng ta sẽ chỉ chuyển chúng vào hàm dưới dạng đối số. 64
www.ZTCprep.com Làm việc với Hàm Các đối số của hàm của chúng ta được tô đậm.
Ngoài thực tế là hiện tại chúng tôi đang sử dụng các đối số, mã này giống hệt với mã
tính toán kích thước lô trước đó. Chúng tôi đã thêm câu lệnh return vào cuối hàm –
câu lệnh này sẽ trả về giá trị của LotSize cho hàm gọi của chúng tôi. Bản thân hàm này
sẽ được đặt ở đâu đó trong tệp chương trình của chúng ta, bên ngoài các hàm start()
và init() hoặc nó sẽ nằm trong một tệp bao gồm bên ngoài. Trong trường hợp sau, câu
lệnh #include ở đầu chương trình sẽ bao gồm tệp để sử dụng trong chương trình của
chúng tôi. Đây là cách chúng ta sử dụng hàm này trong mã. Trước tiên, hãy liệt kê các
biến bên ngoài mà chúng tôi sẽ sử dụng cho cài đặt kích thước lô của mình: 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; Và đây là cách chúng tôi gọi hàm. Dòng
mã này sẽ nằm bên trong hàm start(): double LotSize =
CalcLotSize(DynamicStopLoss,EquityPercent,StopLoss,FixedLotSize); Các biến bên
ngoài của chúng ta được truyền vào hàm dưới dạng đối số. Hàm sẽ tính toán kích
thước lô của chúng ta và giá trị sẽ được lưu trong biến LotSize. Lưu ý rằng biến này
khác với biến LotSize nằm bên trong hàm CalcLotSize(). Cả hai biến đều cục bộ trong
hàm của chúng, vì vậy mặc dù có cùng tên nhưng chúng không phải là cùng một biến.
Chức năng xác minh lô Hãy tiếp tục với mã xác minh lô từ trang 51. Đây sẽ là một
chức năng riêng biệt, trong trường hợp bạn quyết định sử dụng một phương pháp tính
toán kích thước lô thay thế. Bất kể phương pháp xác định kích thước lô là gì, bạn sẽ
muốn xác minh nó trước khi sử dụng nó cho chức năng đặt lệnh: double
VerifyLotSize(double argLotSize) { if(argLotSize <
MarketInfo(Symbol(),MODE_MINLOT)) { argLotSize = MarketInfo (Ký
hiệu(),MODE_MINLOT); } else if(argLotSize > MarketInfo(Symbol(),MODE_MAXLOT)) {
argLotSize = MarketInfo(Symbol(),MODE_MAXLOT); } 65 www.ZTCprep.com LẬP
TRÌNH CỐ VẤN CHUYÊN GIA if(MarketInfo(Symbol(),MODE_LOTSTEP) == 0.1) {
argLotSize = NormalizeDouble(argLotSize,1); } else argLotSize =
NormalizeDouble(argLotSize,2); return(argLotSize); } Đối với hàm này, chúng ta sẽ
chuyển biến có kích thước lô mà chúng ta đã tính toán bằng cách sử dụng
CalcLotSize() làm đối số. Biến đối số argLotSize sau đó được xử lý và trả về hàm gọi.
Chức năng đặt lệnh Bây giờ là lúc lắp ráp chức năng đặt lệnh mua trên thị trường của
chúng ta. Sẽ có một số khác biệt giữa chức năng đặt hàng của chúng tôi và mã mà
chúng tôi đã xem xét trước đó. Đầu tiên, chúng tôi sẽ không đóng đơn hàng trong các
chức năng đặt hàng của mình. Chúng tôi sẽ xử lý việc đóng đơn hàng một cách riêng
biệt. Chúng ta sẽ tạo một chức năng đóng lệnh trong chương tiếp theo. Chúng tôi
cũng sẽ tính toán và sửa đổi mức dừng lỗ và chốt lời bên ngoài chức năng đặt lệnh.
Bởi vì có nhiều cách tính điểm dừng, chúng ta cần giữ cho chức năng đặt lệnh của
mình linh hoạt nhất có thể và không ràng buộc nó với một phương pháp tính điểm
dừng đã xác định trước. Mã sửa đổi đơn hàng đã được chuyển sang một chức năng
riêng. Chúng tôi sẽ đặt lệnh mua ở mức giá thị trường hiện tại bằng cách sử dụng
OrderSend() và nếu lệnh không được đặt, chúng tôi sẽ chạy mã xử lý lỗi từ trang 54.
Trong mọi trường hợp, chúng tôi sẽ trả lại số vé cho chức năng gọi hoặc -1 nếu đơn
hàng không được đặt. Chúng tôi đang chỉ định ký hiệu thứ tự bằng cách sử dụng đối
số argSymbol, thay vì chỉ sử dụng ký hiệu biểu đồ hiện tại. Bằng cách này, nếu bạn
quyết định đặt hàng trên một biểu tượng khác, bạn có thể thực hiện điều đó một cách
dễ dàng. Thay vì sử dụng các biến Giá thầu và Hỏi được xác định trước, chúng ta sẽ
cần sử dụng hàm MarketInfo() với các tham số MODE_ASK và MODE_BID để truy xuất
giá Mua và Bán cho ký hiệu cụ thể đó. Chúng tôi cũng đã chỉ định một giá trị mặc định
cho nhận xét đơn hàng. Đối số argComment có giá trị mặc định là "Lệnh mua". Nếu
không có giá trị nào được chỉ định cho đối số này thì giá trị mặc định sẽ được sử
dụng. Chúng ta giả định rằng kích thước lô và độ trượt giá đã được tính toán và xác
minh trước khi gọi hàm này: 66 www.ZTCprep.com Làm việc với Hàm int
OpenBuyOrder(string argSymbol, double argLotSize, double argSlippage, double
argMagicNumber, string argComment = "Buy Đặt hàng") { while(IsTradeContextBusy())
Ngủ(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); } Trong hàm OrderSend(), hãy lưu ý rằng
chúng tôi đã sử dụng hàm MarketInfo() với tham số MODE_ASK thay cho biến Hỏi
được xác định trước. Điều này sẽ lấy giá Hỏi hiện tại cho ký hiệu tiền tệ được chỉ định
bởi argSymbol. Nếu giao dịch không được thực hiện thành công, quy trình xử lý lỗi sẽ
được thực hiện. Nếu không, phiếu đặt hàng sẽ được trả về chức năng gọi hoặc -1 nếu
lệnh không được đặt. Chức năng đặt lệnh hoàn chỉnh cho lệnh thị trường bán có trong
Phụ lục D. Đặt lệnh chờ Để đặt lệnh chờ, chúng ta cần chuyển các tham số về giá lệnh
chờ cũng như thời gian hết hạn của lệnh. Các đối số argPendingPrice và argExpiration
sẽ được thêm vào hàm. Chúng tôi giả định rằng giá lệnh chờ cũng như mức dừng lỗ và
chốt lời đã được tính toán và xác minh trước khi gọi hàm này. Chức năng đặt lệnh chờ
sẽ đặt lệnh dừng lỗ và chốt lãi đối với lệnh chờ xử lý nên không cần có chức năng sửa
đổi lệnh riêng biệt. 67 www.ZTCprep.com CHƯƠNG TRÌNH CỐ VẤN CHUYÊN GIA Đây
là mã để đặt lệnh dừng mua đang chờ xử lý: int OpenBuyStopOrder(string argSymbol,
double argLotSize, double argPendingPrice, double argStopLoss, double
argTakeProfit, double argSlippage, double argMagicNumber, datetime argExpiration =
0, string argComment = "Lệnh dừng mua") { while(IsTradeContextBusy()) Sleep(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); } Lưu ý rằng chúng tôi
đã chỉ định giá trị mặc định là 0 cho argExpiration. Nếu bạn không sử dụng thời gian
hết hạn của lệnh đang chờ xử lý và bạn muốn sử dụng nhận xét đơn hàng mặc định,
bạn có thể chỉ cần bỏ qua các đối số cho argExpiration và argComment khi gọi hàm.
Ví dụ sau sẽ đặt một lệnh dừng mua không có thời gian hết hạn và nhận xét lệnh mặc
định, "Lệnh dừng mua": int Ticket =
OpenBuyStopOrder(Symbol(),LotSize,PendingPrice,StopLoss,TakeProfit,
UseSlippage,MagicNumber); Chúng tôi đã thêm giá đang chờ xử lý vào nhật ký trong
chức năng xử lý lỗi cũng như thời gian hết hạn nếu có chỉ định. Hàm TimeToStr()
chuyển đổi biến ngày giờ thành định dạng chuỗi có thể đọc được. Các chức năng mở
lệnh dừng bán, giới hạn mua và giới hạn bán giống hệt với chức năng này. Sự khác biệt
duy nhất là tham số loại đơn hàng cho hàm OrderSend() được thay đổi tương ứng. Bạn
có thể xem tất cả các chức năng đặt lệnh chờ trong Phụ lục D. 68 www.ZTCprep.com
Làm việc với Chức năng Chức năng Đóng lệnh Cuối cùng, hãy tạo một hàm để đóng
một lệnh. Chúng ta sẽ sử dụng khối đóng lệnh từ mã ở trang 58. Trong chương tiếp
theo, chúng ta sẽ xem xét các cách đóng nhiều lệnh cùng loại, đây là một phương
pháp đóng lệnh đơn giản hơn. Nhưng trong trường hợp bạn chỉ cần đóng một đơn
hàng, hàm này sẽ thực hiện thủ thuật: 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_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 mua - Lỗi: ",ErrorCode, ": ",ErrDesc); Cảnh
báo(ErrAlert); chuỗi ErrLog = StringConcatenate("Ticket: ",argCloseTicket," Ask: ",
MarketInfo(argSymbol,MODE_ASK)); In(ErrLog); } } return(Đã đóng); } Đối với biến
ClosePrice, chúng tôi sử dụng MarketInfo() để truy xuất giá Bán hiện tại cho loại tiền
được chỉ định bởi argSymbol. Chúng tôi sử dụng các đối số hàm argCloseTicket và
argSlippage tương ứng cho phiếu lệnh đóng và phiếu giảm giá. Nếu lệnh không được
đóng thành công, chúng tôi sẽ chạy khối xử lý lỗi để in số vé và giá Bán hiện tại vào
nhật ký. Mã để đóng lệnh bán sẽ giống hệt nhau, ngoại trừ việc bạn sử dụng Giá thầu
cho biến ClosePrice. Bạn có thể xem chức năng đóng thị trường bán trong Phụ lục D.
69 www.ZTCprep.com CHƯƠNG TRÌNH TƯ VẤN CHUYÊN GIA CHƯƠNG TRÌNH Chức
năng Đóng lệnh chờ Đây là chức năng đóng một lệnh chờ. Điều này sẽ hoạt động trên
tất cả các loại lệnh đang chờ xử lý, mua và bán. bool ClosePendingOrder(string
argSymbol, int argCloseTicket, double argSlippage) {
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(); chuỗi 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); } Hàm tính toán
mức dừng lỗ và chốt lãi Chúng ta sẽ tạo một số hàm ngắn để tính mức dừng lỗ và
chốt lãi như đã thảo luận ở trang 25-30. Chúng tôi sẽ chuyển các biến bên ngoài biểu
thị mức dừng lỗ hoặc chốt lãi tính bằng pip cho hàm của chúng tôi, cũng như giá mở
lệnh. Giá trị trả về của hàm của chúng tôi sẽ là giá dừng lỗ hoặc chốt lãi thực tế. Đây là
hàm tính toán điểm dừng mua tính bằng pip: double CalcBuyStopLoss(string
argSymbol, int argStopLoss, double argOpenPrice) { if(argStopLoss == 0) return(0);
gấp đôi BuyStopLoss = argOpenPrice - (argStopLoss * PipPoint(argSymbol));
return(BuyStopLoss); } 70 www.ZTCprep.com Làm việc với Hàm Trước tiên, chúng ta
sẽ kiểm tra xem liệu mức dừng lỗ hợp lệ có được chuyển cùng với hàm hay không.
Nếu đối số argStopLoss là 0 thì chúng ta trả về giá trị 0 cho hàm gọi, cho biết rằng
không có lệnh dừng lỗ nào được chỉ định. Tiếp theo, chúng tôi tính toán mức dừng lỗ
bằng cách lấy giá mở lệnh trừ đi mức dừng lỗ tính bằng pip. Chúng tôi nhân
argStopLoss với PipPoint() để tính giá trị phân số và trừ giá trị đó khỏi argOpenprice.
Chúng tôi sẽ sử dụng giá Mua hoặc Giá bán (đối với lệnh thị trường) hoặc giá lệnh chờ
dự kiến. Lưu ý rằng chúng tôi không kiểm tra mức dừng hoặc xác minh rằng mức dừng
lỗ là hợp lệ. Chúng tôi sẽ sử dụng một bộ chức năng khác để xác minh hoặc điều
chỉnh giá dừng lỗ nếu cần. Tất nhiên, bạn có thể dễ dàng sửa đổi chức năng này để
xác minh giá dừng lỗ, hiển thị thông báo lỗi hoặc tự động điều chỉnh giá. Đây là hàm
tính toán lợi nhuận mua chốt lời theo pip: double CalcBuyTakeProfit(string argSymbol,
int argTakeProfit, double argOpenPrice) { if(argTakeProfit == 0) return(0); gấp đôi
BuyTakeProfit = OpenPrice + (argTakeProfit * PipPoint(argSymbol));
return(BuyTakeProfit); } Chức năng tính điểm dừng lỗ và chốt lãi cho lệnh bán được liệt
kê trong Phụ lục D. Lưu ý rằng hàm tính điểm dừng bán gần giống với chức năng tính
điểm mua chốt lời ở trên và tương tự như chức năng tính điểm dừng mua và bán. lấy
lợi nhuận. Xác minh mức dừng Chúng tôi sẽ tạo hai bộ hàm để tính toán và xác minh
mức dừng. Đầu tiên sẽ chỉ tính mức dừng ở trên hoặc dưới một mức giá được chỉ định
và trả về giá trị boolean cho biết giá được chỉ định nằm trong hay ngoài mức dừng. Bộ
chức năng thứ hai sẽ tự động điều chỉnh giá sao cho giá nằm ngoài mức dừng, cộng
hoặc trừ một số pip nhất định. Hàm sau đây xác minh xem giá có cao hơn mức dừng
trên hay không (giá mở lệnh cộng với mức dừng). Nếu vậy, hàm trả về true, nếu không
thì trả về false: 71 www.ZTCprep.com EXPERT ADVISOR LẬP TRÌNH bool
VerifyUpperStopLevel(string argSymbol, double argVerifyPrice, double argOpenPrice =
0) { double StopLevel = MarketInfo(argSymbol,MODE_STOPLEVEL) * Point;
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); } Chúng tôi chuyển ký hiệu tiền tệ, giá cần xác minh và giá mở lệnh
(tùy chọn) làm đối số. Theo mặc định, mức dừng được tính tương ứng với giá Hỏi. Nếu
argOpenprice được chỉ định thì mức dừng sẽ được tính tương ứng với mức giá đó. (Sử
dụng điều này khi xác minh mức dừng lỗ và chốt lời cho các lệnh đang chờ xử lý).
Hàm sẽ kiểm tra xem liệu argVerifyPrice có lớn hơn UpperStopLevel hay không. Nếu
có thì giá trị trả về sẽ là true. Nếu không thì sai. Bạn có thể sử dụng chức năng này để
kiểm tra mức dừng lỗ, chốt lãi hoặc giá lệnh chờ hợp lệ mà không cần sửa đổi giá gốc.
Dưới đây là ví dụ trong đó chúng tôi kiểm tra giá dừng lỗ và hiển thị thông báo lỗi nếu
giá không hợp lệ: bool Verify = VerifyUpperStopLevel(Symbol(),SellStopLoss); if(Đã xác
minh == false) Alert("Bán lỗ không hợp lệ!"); Mã để kiểm tra mức dừng dưới mức giá
hiện tại hoặc giá đang chờ xử lý có trong Phụ lục D. Bộ chức năng thứ hai của chúng
tôi tương tự, ngoại trừ việc chúng sẽ tự động điều chỉnh mức dừng lỗ không hợp lệ,
chốt lời hoặc giá lệnh chờ thành giá trị hợp lệ: double Điều chỉnh trênStopLevel (chuỗi
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; 72
www.ZTCprep.com Làm việc với các hàm if(argAdjustPrice <= UpperStopLevel) {
double adjustmentprice = UpperStopLevel + (argAddPips * PipPoint(argSymbol)); }
else Giá điều chỉnh = argAdjustPrice; return(Giá đã điều chỉnh); } Đối số argAdjustPrice
là giá mà chúng tôi sẽ xác minh và điều chỉnh nếu giá đó không hợp lệ. Chúng tôi đã
thêm một tham số tùy chọn mới, argAddPips. Điều này sẽ thêm số pip được chỉ định
vào mức giá dừng khi điều chỉnh giá không hợp lệ. Như trước đây, chúng tôi tính toán
mức dừng, liên quan đến giá Bán hoặc thông số argOpenPrice. Nếu tham số
argAdjustPrice nằm trong mức dừng (tức là không hợp lệ), giá sẽ được điều chỉnh sao
cho nằm ngoài mức dừng theo số pip được chỉ định bởi argAddPips. Nếu giá do
argAdjustprice chỉ định là hợp lệ thì giá đó sẽ được chuyển trở lại hàm gọi. Trong mọi
trường hợp, giá trị trả về là giá trị bạn muốn sử dụng cho mức chốt lời, dừng lỗ hoặc
giá đặt lệnh chờ xử lý. Chúng tôi sẽ sử dụng các chức năng này trong cuốn sách này
để xác minh các mức dừng và điều chỉnh giá của chúng tôi cho phù hợp. Bạn có thể
tìm thấy các hàm tính toán và xác minh mức dừng thấp hơn trong Phụ lục D. Thêm
mức dừng lỗ và chốt lãi Để phù hợp với ý tưởng của chúng tôi là giữ cho các hàm tập
trung vào các nhiệm vụ đơn giản và riêng biệt, chúng tôi đã chuyển phần sửa đổi lệnh
của mình sang một hàm riêng biệt. Chức năng này sẽ thêm hoặc sửa đổi mức dừng lỗ
và chốt lời theo lệnh đã chỉ định. Chúng tôi giả sử giá dừng lỗ và chốt lời đã được tính
toán và xác minh: bool AddStopProfit(int argTicket, double argStopLoss, double
argTakeProfit) { if(argStopLoss == 0 && argTakeProfit == 0) return(false);
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); 73
www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA // 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); } Trước tiên, chúng tôi kiểm tra
xem liệu giá dừng lỗ hoặc giá chốt lãi đã được cung cấp hay chưa. Nếu không, chúng
ta sẽ thoát khỏi chức năng. Nếu không thì, chúng tôi sẽ sửa đổi lệnh bằng cách sử
dụng lệnh dừng lỗ và chốt lãi đã được chuyển cho hàm. Chức năng xử lý lỗi sẽ chạy
nếu sửa đổi đơn hàng không thành công. Chức năng này sẽ hoạt động trên tất cả các
loại đơn đặt hàng. Sử dụng Bao gồm các tệp Để sắp xếp các hàm của chúng tôi để dễ
dàng đưa vào các tệp mã nguồn, chúng tôi sẽ đặt các hàm vào một tệp bao gồm. Tệp
bao gồm có thể bao gồm các khai báo hàm, hàm đã nhập và bất kỳ biến toàn cục
hoặc bên ngoài nào mà bạn muốn đưa vào Expert Advisor. Bao gồm các tập tin không
yêu cầu cú pháp đặc biệt. Bạn khai báo các hàm và biến trong tệp đính kèm giống như
cách bạn làm trong bất kỳ tệp mã nguồn nào. Bao gồm các tệp không được có hàm
init(), start() hoặc deinit(). Tệp phải có phần mở rộng .mqh và nằm trong thư mục
\experts\include. Tất cả các hàm chúng tôi tạo trong cuốn sách này sẽ được đặt trong
một tệp bao gồm có tên Bao gồmExample.mqh. Nội dung của tệp này được liệt kê
trong Phụ lục D. Sử dụng Thư viện Thư viện là một tập hợp các hàm được biên dịch.
Trong khi tệp bao gồm là tệp mã nguồn có nội dung được "đưa vào" trong tệp thực thi
thì thư viện là tệp thực thi riêng biệt có chứa các hàm được nhập. Do đó, bạn phải có
cả trình cố vấn chuyên gia và thư viện có thể thực thi được để chạy EA của mình. 74
www.ZTCprep.com Làm việc với các hàm Thư viện được lưu trữ trong thư mục
\experts\libraries. Các tệp mã nguồn có phần mở rộng .mq4 và các tệp thực thi có
phần mở rộng .ex4. Thư viện không có hàm start(), init() hoặc deinit(). Để khai báo
một tệp là thư viện, bạn phải đặt chỉ thị tiền xử lý thư viện #property ở đầu tệp. Ưu
điểm của thư viện là chúng được biên dịch, vì vậy nếu bạn cần phân phối một thư viện
hàm, bạn có thể làm như vậy mà không để lộ tài sản trí tuệ của mình như khi bạn phân
phối một tệp đính kèm. Bạn cũng có thể sửa lỗi cho thư viện mà không cần phải biên
dịch lại chuyên gia cố vấn của mình – miễn là bạn không thực hiện bất kỳ thay đổi nào
đối với khai báo hàm, chẳng hạn như thêm và xóa đối số hoặc hàm. Thư viện cũng có
một số nhược điểm. Vì chúng đã được biên dịch nên trình biên dịch không thể kiểm tra
xem các tham số có đúng hay không. Bạn không thể chỉ định giá trị mặc định cho
tham số trong hàm thư viện, điều đó có nghĩa là bạn sẽ cần chỉ định giá trị cho mọi đối
số trong lệnh gọi hàm. Bạn không thể sử dụng các biến bên ngoài trong thư viện hoặc
tạo các biến có phạm vi toàn cầu mà cố vấn chuyên gia của bạn có thể truy cập. Bạn
sẽ cần sử dụng lệnh #import để nhập các hàm thư viện vào Expert Advisor của mình.
Nếu thư viện chứa nhiều hàm, tốt nhất bạn nên tạo một tệp đính kèm với câu lệnh
#import. Điều này làm tăng số lượng tệp bạn cần làm việc. Trừ khi bạn có lý do chính
đáng để sử dụng thư viện, bạn nên sử dụng các tệp đính kèm để lưu trữ các hàm của
mình. Bạn cũng có thể nhập các hàm từ Windows DLL bằng cách sử dụng chỉ thị
#import. Tệp bao gồm WinUser32.mqh trong \experts\includes có nhiều ví dụ được
sử dụng cho hàm MessageBox(). (Chúng ta sẽ thảo luận về hàm MessageBox() trong
chương 8). Sử dụng hàm DLL là cách sử dụng nâng cao mà chúng tôi sẽ không đề cập
ở đây. Trên website MQL4 có bài viết về cách sử dụng DLL dành cho những ai quan
tâm. Một Expert Advisor Đơn giản (có Chức năng) Đây là mã nguồn Expert Advisor
của chúng tôi, như nó xuất hiện trong tệp mã nguồn. Chúng ta giả định rằng các hàm
chúng ta tạo trong chương này được khai báo trong tệp includeExample.mqh, nội
dung của nó được liệt kê trong Phụ lục D. // Bộ tiền xử lý #include // Các 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; 75 www.ZTCprep.com CHUYÊN GIA TƯ VẤN LẬP TRÌNH
extern double StopLoss = 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); } // 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); // Tính kích thước lô double LotSize =
CalcLotSize(DynamicLotSize,EquityPercent,StopLoss,FixedLotSize); LotSize =
VerifyLotSize(LotSize); // Lệnh mua if(FastMA > SlowMA && BuyTicket == 0) {
if(SellTicket > 0) int Closed = CloseSellOrder(Symbol(),SellTicket,UseSlippage);
BánTicket = 0; BuyTicket =
OpenBuyOrder(Symbol(),LotSize,UseSlippage,MagicNumber); if(BuyTicket > 0 &&
(StopLoss > 0 || TakeProfit > 0)) { OrderSelect(BuyTicket,SELECT_BY_TICKET); gấp đôi
OpenPrice = OrderOpenPrice(); 76 www.ZTCprep.com Làm việc với các hàm double
BuyStopLoss = CalcBuyStopLoss(Symbol(),StopLoss,OpenPrice); if(BuyStopLoss > 0)
{ BuyStopLoss = Điều chỉnhBelowStopLevel(Symbol(),BuyStopLoss,5); } double
BuyTakeProfit = CalcBuyTakeProfit(Symbol(),TakeProfit,OpenPrice); if(BuyTakeProfit >
0) { BuyTakeProfit = Điều chỉnhAboveStopLevel(Symbol(),BuyTakeProfit,5); }
AddStopProfit(MuaTicket,BuyStopLoss,BuyTakeProfit); } } // Lệnh bán if(FastMA <
SlowMA && SellTicket == 0) { if(BuyTicket > 0) Closed =
CloseBuyOrder(Symbol(),BuyTicket,Slippage); MuaVé = 0; 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); } double SellTakeProfit =
CalcSellTakeProfit(Symbol(),TakeProfit,OpenPrice); if(SellTakeProfit > 0) {
SellTakeProfit = Điều chỉnhBelowStopLevel(Symbol(),SellTakeProfit,5); }
AddStopProfit(SellTicket,SellStopLoss,SellTakeProfit); } } trả về(0); } 77
www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Chúng tôi bắt đầu bằng cách
đưa vào tệp có các chức năng của chúng tôi trong đó, trong trường hợp này là Bao
gồmExample.mqh. Việc khai báo biến và nội dung của hàm init() vẫn giống như trước.
Khi bắt đầu hàm start(), chúng ta sử dụng CalcLotSize() và VerifyLotSize() để tính toán
và xác minh kích thước lô của mình. Trong khối lệnh mua và bán, chúng tôi sử dụng
CloseBuyOrder() và CloseSellOrder() để đóng lệnh ngược lại. Các lệnh mới của chúng
tôi được mở bằng OpenBuyOrder() hoặc OpenSellOrder(). Trước khi tính toán mức
dừng lỗ và chốt lãi, chúng tôi kiểm tra xem lệnh đã được mở chưa và mức StopLoss
hoặc TakeProfit đã được chỉ định hay chưa. Chúng ta truy xuất giá mở lệnh bằng cách
sử dụng OrderSelect() và OrderOpenPrice(). Sau đó, chúng tôi tính toán mức dừng lỗ
bằng cách sử dụng CalcBuyStopLoss() hoặc CalcSellStopLoss() và chốt lãi bằng cách
sử dụng CalcBuyTakeProfit() hoặc CalcSellTakeProfit(). Chúng tôi kiểm tra xem liệu
mức dừng lỗ hoặc chốt lời có lớn hơn 0 hay không và sử dụng các hàm Điều chỉnh
trênStopLevel() và Điều chỉnhBelowStopLevel() để xác minh mức dừng lỗ và chốt lời
của chúng tôi. Cuối cùng, chúng ta chuyển những mức giá đó cho hàm
AddOrderProfit(), hàm này sẽ thêm mức dừng lỗ và chốt lời cho lệnh. EA ở trên thực
hiện chính xác điều tương tự như mã bắt đầu ở trang 51, nhưng dễ đọc hơn nhiều.
Bằng cách chia mã thành các hàm, chúng tôi đã loại bỏ mã nguồn của mình và làm
cho EA của chúng tôi dễ quản lý hơn. Chúng tôi sẽ bổ sung thêm một số tính năng cho
chuyên gia cố vấn này trước khi kết thúc cuốn sách. Bạn có thể xem mã hoàn chỉnh
trong Phụ lục C. Công việc ban đầu trong việc tạo các hàm này sẽ mất chút thời gian,
nhưng về lâu dài sẽ giúp bạn tiết kiệm thời gian vì bạn sẽ dễ dàng tạo mẫu các ý tưởng
giao dịch và tạo ra các chuyên gia cố vấn đang hoạt động. trong một khoảng thời gian
ngắn. 78 www.ZTCprep.com Làm việc với các hàm 79 www.ZTCprep.com LẬP TRÌNH
CHUYÊN GIA TƯ VẤN Chương 5 Quản lý đơn hàng Bạn đã được giới thiệu về hàm
OrderSelect() trong chương 2. Trong phần này, chúng ta sẽ sử dụng hàm OrderSelect()
, cùng với các toán tử chu trình for và while, để lặp qua nhóm đơn hàng và truy xuất
thông tin đơn hàng. Phương pháp này sẽ được sử dụng để đóng nhiều lệnh, thêm
điểm dừng cuối, đếm số lượng lệnh mở, v.v. Vòng lặp thứ tự Toán tử for Toán tử for
được sử dụng để lặp qua một khối mã với số lần xác định trước. Chúng ta khai báo
một biến số nguyên để sử dụng làm bộ đếm và gán cho nó một giá trị bắt đầu. Chúng
tôi chỉ ra điều kiện, nếu đúng, sẽ khiến vòng lặp chạy. Chúng tôi cũng chỉ ra một biểu
thức để tăng biến đếm. Đây là một ví dụ về vòng lặp for: for(int Counter = 1; Counter
<= 3; Counter++) { // Code to loop } Biểu thức đầu tiên, int Counter = 1, khởi tạo biến
Counter của chúng ta với giá trị là 1. biểu thức thứ hai, Bộ đếm <= 3, là điều kiện, nếu
đúng, sẽ thực thi mã bên trong dấu ngoặc nhọn. Nếu sai, vòng lặp kết thúc và việc
thực thi tiếp tục sau dấu ngoặc nhọn cuối (}). Biểu thức thứ ba, Counter++, có nghĩa là
"tăng giá trị của Counter lên một." Biểu thức Counter-- sẽ giảm giá trị đi một và
Counter+2 sẽ tăng thêm hai. Mỗi khi vòng lặp hoàn thành, biến đếm sẽ tăng hoặc
giảm. Trong lần lặp tiếp theo của vòng lặp, đối số thứ hai, trong trường hợp này là
Counter<= 3, được đánh giá lại. Lưu ý rằng không có dấu chấm phẩy sau biểu thức thứ
ba. Ví dụ trên sẽ thực hiện vòng lặp ba lần. Sau mỗi lần lặp, bộ đếm được tăng thêm
một và sau lần lặp thứ ba, vòng lặp sẽ kết thúc. 80 www.ZTCprep.com Quản lý đơn
hàng Toán tử while Toán tử while là một phương pháp lặp đơn giản hơn trong MQL.
Vòng lặp for là tốt nhất nếu bạn biết chính xác số lần bạn định thực hiện vòng lặp. Tuy
nhiên, nếu bạn không chắc chắn về số lần lặp thì vòng lặp while sẽ phù hợp hơn. Đây là
một ví dụ về vòng lặp while: while(Something == true) { // Loop code } Ví dụ trực tiếp
này sử dụng một biến boolean có tên là Something. Nếu Something bằng true, vòng
lặp sẽ thực thi. Tất nhiên, nếu giá trị của Something không bao giờ thay đổi thì vòng
lặp sẽ chạy vô tận. Vì vậy, cần phải có điều kiện để thay đổi giá trị của Something tại
một thời điểm nào đó trong vòng lặp. Khi điều kiện này đúng, Something được thay
đổi thành false và vòng lặp sẽ ngừng thực thi. Bạn cũng có thể tăng một biến, giống
như cách bạn sử dụng toán tử for: int Counter = 1; while(Bộ đếm <= 3) { Bộ đếm++; }
Đoạn mã này sẽ thực thi chính xác như vòng lặp for ở trên! Vòng lặp đặt hàng Đây là
mã chúng ta sẽ sử dụng để lặp qua nhóm các lệnh đang mở: for(Counter = 0; Counter
<= OrderTotal()-1; Counter++) { OrderSelect(Counter,SELECT_BY_POS); // Đánh giá
điều kiện } Chúng ta sẽ đặt giá trị của Counter thành 0 và lặp lại vòng lặp miễn là
Counter nhỏ hơn hoặc bằng giá trị của OrderTotal(), trừ đi một. Bộ đếm sẽ tăng thêm 1
sau mỗi lần lặp của vòng lặp. 81 www.ZTCprep.com CHƯƠNG TRÌNH TƯ VẤN
CHUYÊN NGHIỆP OrderTotal() là một hàm trả về số lượng lệnh hiện đang mở. Tại sao
chúng ta trừ 1 từ giá trị của OrderTotal()? Hãy giải thích cách hoạt động của nhóm
lệnh: Nhóm lệnh chứa tất cả các lệnh hiện đang mở trong phần mềm đầu cuối của
chúng tôi, bao gồm các lệnh được đặt thủ công cũng như các lệnh do chuyên gia tư
vấn đặt. Các chỉ mục thứ tự được đánh số bắt đầu từ số 0. Nếu có một lệnh mở, chỉ số
của nó là 0. Khi lệnh thứ hai được mở, chỉ số của nó là 1. Nếu lệnh thứ ba được mở, chỉ
số của lệnh đó sẽ là 2, v.v. Chỉ mục 0 là thứ tự cũ nhất và chỉ mục 2 là thứ tự mới nhất.
OrderTotal() sẽ trả về số lượng lệnh hiện đang mở. Trong ví dụ trên, chúng tôi có ba
lệnh mở. Nhưng vì chỉ số thứ tự của chúng ta bắt đầu từ 0 nên chúng ta muốn biến bộ
đếm chỉ đếm đến 2. Giá trị của Bộ đếm phải tương ứng với các số chỉ mục thứ tự của
chúng ta, vì vậy đó là lý do tại sao chúng ta phải trừ 1 từ OrderTotal(). Khi một đơn
hàng trong nhóm lệnh mở bị đóng, mọi đơn hàng mới hơn trong nhóm sẽ bị giảm chỉ
số đơn hàng. Ví dụ: nếu đơn hàng có chỉ số 0 bị đóng thì đơn hàng có chỉ số 1 sẽ trở
thành chỉ mục 0 và chỉ số đơn hàng 2 trở thành chỉ mục 1. Điều này rất quan trọng khi
chúng tôi đóng đơn hàng và chúng tôi sẽ sớm đề cập đến vấn đề này chi tiết hơn.
Quay lại vòng lặp đặt hàng của chúng ta: Câu lệnh OrderSelect() sử dụng biến Counter
làm chỉ mục vị trí đặt hàng. Như đã giải thích ở trên, chúng tôi sẽ tăng dần nhóm đơn
hàng từ đơn hàng cũ nhất đến đơn hàng mới nhất. Tham số SELECT_BY_POS cho biết
rằng chúng ta đang chọn đơn hàng theo vị trí của nó trong nhóm đơn hàng, trái ngược
với số phiếu của nó. Đối với lần lặp đầu tiên của vòng lặp này, Bộ đếm sẽ bằng 0 và
chúng tôi sẽ chọn đơn hàng cũ nhất từ ​nhóm đơn hàng bằng cách sử dụng
OrderSelect(). Sau đó, chúng tôi có thể kiểm tra thông tin đơn hàng bằng cách sử
dụng các hàm như OrderTicket() hoặc OrderStopLoss() và sửa đổi hoặc đóng đơn
hàng nếu cần. Đếm đơn hàng Việc tìm hiểu EA của chúng tôi đã mở bao nhiêu đơn
hàng và thuộc loại nào thường rất hữu ích. Chúng ta sẽ tạo một số hàm đếm đơn hàng
để đếm số lượng đơn hàng đang mở hiện tại, dựa trên loại đơn hàng. Hàm sau sẽ đếm
tổng số lệnh mở: int TotalOrderCount(string argSymbol, int argMagicNumber) { int
OrderCount; for(Bộ đếm = 0; Bộ đếm <= OrderTotal()-1; Bộ đếm++) { OrderSelect(Bộ
đếm,SELECT_BY_POS); 82 www.ZTCprep.com Quản lý đơn hàng
if(OrderMagicNumber() == argMagicNumber && OrderSymbol() == argSymbol) {
OrderCount++; } } return(Đếm đơn hàng); } Chúng tôi đã đặt tên cho hàm đếm đơn
hàng là TotalOrderCount(). Nó sẽ trả về một giá trị số nguyên cho biết có bao nhiêu
lệnh hiện đang được mở trên biểu tượng biểu đồ đã chỉ định khớp với số ma thuật mà
chúng ta đã chuyển làm đối số hàm. Chúng ta bắt đầu bằng việc khai báo biến
OrderCount. Vì chúng ta chưa chỉ ra giá trị ban đầu nên OrderCount sẽ được khởi tạo
là 0. Bạn sẽ nhận ra toán tử for và hàm OrderSelect() từ phần trước. Vì nhóm lệnh
chứa tất cả các lệnh đang mở, bao gồm cả các lệnh được đặt bởi EA khác, nên chúng
tôi cần xác định đơn hàng nào đã được EA của chúng tôi đặt. Trước tiên, chúng tôi
kiểm tra OrderSymbol() của thứ tự đã chọn và đảm bảo rằng nó khớp với đối số
argSymbol. Chúng tôi kiểm tra con số kỳ diệu trên đơn đặt hàng. Nếu
OrderMagicNumber() khớp với đối số argMagicNumber, chúng ta có thể khá chắc
chắn rằng đơn hàng này được EA này đặt. Miễn là người dùng không chạy hai EA trên
cùng một biểu tượng tiền tệ có cùng số ma thuật, chúng tôi có thể chắc chắn rằng đơn
hàng này được đặt bởi EA này. Khi chạy nhiều chuyên gia cố vấn trên cùng một công
cụ, hãy cẩn thận để đảm bảo rằng bạn đang sử dụng một con số kỳ diệu duy nhất trên
mỗi EA. Nếu thứ tự khớp với cả số ma thuật và biểu tượng biểu đồ của chúng tôi, giá
trị của OrderCount sẽ tăng thêm một. Sau khi lặp qua tất cả các đơn hàng trong nhóm
đơn hàng, chúng tôi trả về giá trị của OrderCount cho hàm gọi. Đây là một ví dụ về
cách chúng tôi sử dụng mã này trong mã: if(TotalOrderCount(Symbol(),MagicNumber)
> 0 && CloseOrders == true) { // Đóng tất cả các đơn hàng } Nếu có đơn hàng được mở
bởi EA này và giá trị của CloseOrders là đúng (chúng ta giả sử giá trị này được đặt ở
một nơi khác trong chương trình), khi đó mã bên trong dấu ngoặc nhọn sẽ chạy, mã
này sẽ đóng tất cả các lệnh đang mở. 83 www.ZTCprep.com CHƯƠNG TRÌNH CỐ VẤN
CHUYÊN GIA Hãy sửa đổi quy trình đếm đơn hàng của chúng ta để chỉ tính các đơn
đặt hàng thị trường mua: int BuyMarketCount(string argSymbol, int argMagicNumber)
{ int OrderCount; for(Bộ đếm = 0; Bộ đếm <= OrderTotal()-1; Bộ đếm++) {
OrderSelect(Bộ đếm,SELECT_BY_POS); if(OrderMagicNumber() == argMagicNumber
&& OrderSymbol() == argSymbol && OrderType() == OP_BUY) { OrderCount++; } }
return(Đếm đơn hàng); } Mã này giống hệt với mã trước, ngoại trừ việc chúng tôi đã
thêm hàm OrderType() để kiểm tra loại đơn hàng của đơn hàng hiện được chọn.
OP_BUY là hằng số biểu thị lệnh mua trên thị trường. Để đếm các loại đơn hàng khác,
chỉ cần thay thế OP_BUY bằng hằng số loại đơn hàng thích hợp và đổi tên hàm để
phản ánh loại đơn hàng. Chúng tôi khuyên bạn nên tạo chức năng đếm đơn hàng cho
mọi loại đơn hàng. Bạn có thể xem mã cho tất cả các chức năng đếm đơn hàng trong
Phụ lục D. Đóng nhiều đơn hàng. Thường thì không, chúng ta sẽ cần đóng nhiều đơn
hàng cùng loại. Chúng tôi sẽ kết hợp vòng lặp đặt hàng với quy trình đóng đơn hàng
để đóng nhiều đơn hàng cùng một lúc. Hàm này sẽ đóng tất cả các lệnh thị trường
mua được đặt bởi cố vấn chuyên gia của chúng tôi: 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); 84 www.ZTCprep.com Bool quản lý đơn hàng
Closed = OrderClose(CloseTicket,CloseLots,ClosePrice,argSlippage,Red); // Xử lý lỗi
if(Closed == false) { 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--; } } } Lưu ý rằng chúng ta đang sử dụng
void làm kiểu dữ liệu hàm. Chúng tôi đã xác định rằng không có dữ liệu hữu ích nào để
trả về từ hàm này, vì vậy chúng tôi không yêu cầu toán tử trả về trong hàm. Bạn sẽ
nhận ra vòng lặp for và hàm OrderSelect() từ mã vòng lặp đơn hàng của chúng tôi.
Chúng tôi sẽ lặp qua nhóm đơn hàng và kiểm tra từng đơn hàng để xem liệu chúng tôi
có cần đóng nó hay không. Nếu lệnh hiện tại là lệnh thị trường mua, như được chỉ ra
bởi OP_BUY và nếu nó khớp với biểu tượng biểu đồ và đối số số ma thuật của chúng
tôi, chúng tôi sẽ tiến hành đóng lệnh. Chúng ta gọi hàm OrderTicket() để lấy số vé cho
đơn hàng hiện tại. Từ đây, mã của chúng tôi giống hệt với mã đóng thị trường mua ở
các chương trước. Hãy lưu ý câu lệnh cuối cùng: Counter--. Nếu lệnh được đóng đúng
cách, biến Counter sẽ giảm đi một. Chúng tôi đã giải thích trước đó rằng khi một lệnh
được đóng, tất cả các lệnh đằng sau lệnh đó sẽ có chỉ số giảm đi một. Nếu chúng ta
không giảm biến đếm sau khi đóng một lệnh thì các lệnh tiếp theo sẽ bị bỏ qua. Có
một lý do rất chính đáng khiến chúng tôi lặp lại các lệnh từ cũ nhất đến mới nhất: Các
quy định của NFA có hiệu lực vào mùa hè năm 2009 đối với các nhà môi giới Hoa Kỳ
yêu cầu nhiều lệnh đặt trên cùng một ký hiệu tiền tệ phải được đóng theo thứ tự chúng
được đặt. Đây được gọi là quy tắc FIFO (nhập trước, xuất trước). Việc lặp lại các lệnh
từ cũ nhất đến mới nhất đảm bảo rằng chúng ta tuân thủ quy tắc FIFO khi đóng lệnh.
85 www.ZTCprep.com LẬP TRÌNH TƯ VẤN CHUYÊN GIA Để đóng các lệnh bán trên thị
trường bằng cách sử dụng mã trên, chỉ cần thay đổi loại lệnh thành OP_SELL và
ClosePrice thành Giá bán của biểu tượng. Chức năng đóng lệnh bán có thể được xem
trong Phụ lục D. Hãy kiểm tra mã để đóng nhiều lệnh chờ. Ví dụ này sẽ đóng tất cả các
lệnh dừng mua. Sự khác biệt giữa mã này và mã để đóng lệnh thị trường mua ở trên là
chúng tôi chỉ định OP_BUYSTOP làm loại lệnh và chúng tôi sử dụng OrderDelete() để
đóng lệnh. void CloseAllBuyStopOrders(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_BUYSTOP) { // Xóa đơn hàng
int CloseTicket = OrderTicket(); while(IsTradeContextBusy()) Ngủ(10); bool Đã đóng =
OrderDelete(CloseTicket,Red); // Xử lý lỗi if(Closed == false) { 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--; } } } Mã này sẽ hoạt động với tất cả các loại
lệnh chờ - chỉ cần thay đổi so sánh loại lệnh thành loại lệnh bạn muốn đóng. Bạn có
thể xem chức năng đóng lệnh cho tất cả các lệnh đang chờ xử lý trong Phụ lục D. 86
www.ZTCprep.com Quản lý lệnh Trailing Stop Chúng tôi cũng có thể sử dụng vòng lặp
lệnh của mình để sửa đổi nhiều lệnh. Một ví dụ phổ biến về điều này là điểm dừng
cuối. Lệnh dừng lỗ di chuyển mức dừng lỗ lên hoặc xuống theo giá đặt lệnh khi lệnh
thu được lợi nhuận. Điều này "khóa" lợi nhuận và cung cấp khả năng bảo vệ thua lỗ
tuyệt vời. Điểm dừng cuối được biểu thị bằng số pip tối đa. Ví dụ: nếu điểm dừng treo
của bạn là 50 pip, điểm dừng lỗ sẽ không bao giờ cách giá của bạn quá 50 pip. Nếu
giá đảo chiều và lợi nhuận giảm, mức dừng lỗ sẽ giữ nguyên. Điểm dừng chỉ di chuyển
theo hướng có lợi – không bao giờ đảo ngược. Khi sửa đổi điểm dừng treo, chúng ta
phải kiểm tra xem liệu khoảng cách tính bằng pip giữa giá hiện tại và mức dừng lỗ hiện
tại có lớn hơn điểm dừng treo hay không. Nếu vậy, mức dừng lỗ sẽ được sửa đổi sao
cho khoảng cách từ giá hiện tại tính bằng pip bằng số pip trong cài đặt dừng treo.
Điểm dừng cuối được tính tương ứng với giá đóng cửa, đó là Giá thầu cho lệnh mua và
Lệnh bán. Lưu ý rằng giá này trái ngược với giá mở cửa. Hãy kiểm tra mã để sửa đổi
dấu dừng cuối. Đầu tiên, chúng ta khai báo biến bên ngoài cho cài đặt Trailing Stop:
extern double TrailingStop = 50; Mã này kiểm tra tất cả các lệnh mua trên thị trường và
sửa đổi mức dừng lỗ nếu cần: for(int Counter = 0; Counter <= OrderTotal()-1;
Counter++) { OrderSelect(Counter,SELECT_BY_POS); gấp đôi MaxStopLoss =
MarketInfo(Symbol(),MODE_BID) - (TrailingStop * PipPoint(Symbol())); MaxStopLoss =
NormalizeDouble(MaxStopLoss,MarketInfo(OrderSymbol(),MODE_DIGITS)); double
CurrentStop = NormalizeDouble(OrderStopLoss(),
MarketInfo(OrderSymbol(),MODE_DIGITS)); // Sửa đổi Dừng nếu(OrderMagicNumber()
== MagicNumber && OrderSymbol() == Symbol() && OrderType() == OP_BUY &&
CurrentStop < MaxStopLoss) { bool Trailed =
OrderModify(OrderTicket(),OrderOpenPrice(),MaxStopLoss, OrderTakeProfit (),0); 87
www.ZTCprep.com LẬP TRÌNH CỐ VẤN CHUYÊN GIA // Xử lý lỗi if(Trailed == false) {
ErrorCode = GetLastError(); chuỗi ErrDesc = ErrorDescription(ErrorCode); chuỗi
ErrAlert = StringConcatenate("Buy Trailing Stop - Error ", ErrorCode,": ",ErrDesc); Cảnh
báo(ErrAlert); string ErrLog = StringConcatenate("Bid:
"MarketInfo(Symbol(),MODE_BID), " Ticket: ",CloseTicket," Stop: ",OrderStopLoss(),"
Trail: ", MaxStopLoss); In(ErrLog); } } } Sau khi chọn đơn hàng từ nhóm với
OrderSelect(), chúng tôi xác định khoảng cách dừng lỗ tối đa bằng cách trừ đi cài đặt
điểm dừng treo của chúng tôi, nhân với PipPoint(), từ Giá thầu hiện tại. Điều này được
lưu trữ trong biến MaxStopLoss. Chúng tôi sử dụng hàm MQL NormalizeDouble() để
làm tròn biến MaxStopLoss thành đúng số chữ số sau dấu thập phân. Giá trong
MetaTrader có thể được trích dẫn lên tới tám chữ số thập phân. Bằng cách sử dụng
NormalizeDouble(), chúng tôi làm tròn số đó xuống còn 4 hoặc 5 chữ số (2-3 chữ số
cho các cặp JPY). Tiếp theo, chúng tôi truy xuất mức dừng lỗ của lệnh hiện được chọn
và làm tròn nó bằng cách sử dụng NormalizeDouble() để đảm bảo. Chúng tôi gán giá
trị này cho biến CurrentStop. Sau đó, chúng tôi kiểm tra xem liệu thứ tự hiện tại có cần
được sửa đổi hay không. Nếu số ma thuật, ký hiệu và loại lệnh khớp nhau và mức
dừng lỗ hiện tại (CurrentStop) nhỏ hơn MaxStopLoss thì chúng tôi sẽ sửa đổi mức
dừng lỗ của lệnh. Chúng ta chuyển biến MaxStopLoss làm điểm dừng lỗ mới cho hàm
OrderModify(). Nếu hàm OrderModify() không thành công, quy trình xử lý lỗi sẽ chạy
và thông tin về giá hiện tại, số vé, mức dừng lỗ hiện tại và mức dừng lỗ đã sửa đổi sẽ
được in vào nhật ký. Các điều kiện dừng treo cho lệnh bán là khác nhau và cần được
giải quyết riêng. Dưới đây là các điều kiện để sửa đổi lệnh bán: // Sửa đổi Dừng
if(OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol() &&
OrderType() == OP_SELL && (CurrentStop > MaxStopLoss || CurrentStop == 0) ) 88
www.ZTCprep.com Quản lý đơn hàng Lưu ý điều kiện (CurrentStop > MaxStopLoss ||
CurrentStop == 0). Nếu không có lệnh dừng lỗ nào được đặt cùng với lệnh thì điều kiện
CurrentStop > MaxStopLoss sẽ không bao giờ đúng, bởi vì MaxStopLoss sẽ không bao
giờ nhỏ hơn 0. Do đó, chúng tôi thêm điều kiện OR, CurrentStop == 0. Nếu mức dừng lỗ
của lệnh hiện tại là 0 (không có mức dừng lỗ), thì miễn là các điều kiện còn lại là đúng
thì điểm dừng cuối sẽ được đặt. Lợi nhuận tối thiểu Hãy nâng cao điểm dừng cuối của
chúng tôi bằng cách thêm mức lợi nhuận tối thiểu. Trong ví dụ trên, điểm dừng cuối sẽ
có hiệu lực ngay lập tức. Nếu bạn đặt mức dừng lỗ ban đầu là 100 pip và điểm dừng
cuối của bạn là 50 pip, thì mức dừng lỗ sẽ được đặt thành 50 pip ngay lập tức, làm
mất hiệu lực mức dừng lỗ 100 pip ban đầu của bạn. Việc thêm mức lợi nhuận tối thiểu
sẽ cho phép bạn đặt mức dừng lỗ ban đầu, đồng thời trì hoãn lệnh dừng lỗ cho đến khi
đạt được mức lợi nhuận xác định. Trong ví dụ này, giả sử mức dừng lỗ ban đầu là 100
pip được đặt khi đặt lệnh. Chúng tôi đang sử dụng lệnh dừng lỗ là 50 pip, với mức lợi
nhuận tối thiểu là 50 pip. Khi lợi nhuận của lệnh đạt 50 pip, mức dừng lỗ sẽ được điều
chỉnh để hòa vốn. Hãy thêm một biến bên ngoài cho cài đặt lợi nhuận tối thiểu của
chúng ta: extern int TrailingStop = 50; extern int Lợi nhuận tối thiểu = 50; Hàm sau sửa
đổi mức dừng lỗ cho tất cả các lệnh mua trên thị trường, kiểm tra lợi nhuận tối thiểu
trước khi thực hiện: void BuyTrailingStop(string argSymbol, int argTrailingStop, int
argMinProfit, int argMagicNumber) { for(int Counter = 0; Counter <= OrderTotal() -1; Bộ
đếm++) { OrderSelect(Bộ đếm,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) - (TrailingStop *
PipPoint(argSymbol)); MaxStopLoss = NormalizeDouble(MaxStopLoss,
MarketInfo(OrderSymbol(),MODE_DIGITS)); double CurrentStop =
NormalizeDouble(OrderStopLoss(), MarketInfo(OrderSymbol(),MODE_DIGITS)); 89
www.ZTCprep.com CHUYÊN GIA TƯ VẤN LẬP TRÌNH double PipsProfit =
MarketInfo(argSymbol,MODE_BID) - OrderOpenPrice(); gấp đôi MinProfit = Lợi nhuận
tối thiểu * 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) { ErrorCode = GetLastError(); chuỗi ErrDesc =
ErrorDescription(ErrorCode); chuỗi ErrAlert = StringConcatenate("Buy Trailing Stop -
Error ", ErrorCode,": ",ErrDesc); Cảnh báo(ErrAlert); chuỗi ErrLog =
StringConcatenate("Bid: ", MarketInfo(argSymbol,MODE_BID)," Ticket: ",CloseTicket, "
Stop: ",OrderStopLoss()," Trail: ",MaxStopLoss); In(ErrLog); } } } } Chúng tôi tính toán lợi
nhuận của đơn đặt hàng hiện tại tính bằng pip bằng cách trừ OrderOpenPrice() khỏi
giá Mua hiện tại và lưu giá trị đó vào biến PipsProfit. Chúng tôi so sánh điều đó với cài
đặt lợi nhuận tối thiểu của chúng tôi, được nhân với PipPoint() và được lưu trữ trong
biến MinProfit. Nếu lợi nhuận hiện tại tính bằng pip (PipsProfit) lớn hơn hoặc bằng lợi
nhuận tối thiểu của chúng tôi (MinProfit) và tất cả các điều kiện khác đều đúng thì
điểm dừng sẽ được sửa đổi. Điểm dừng cuối cùng với cài đặt lợi nhuận tối thiểu linh
hoạt hơn nhiều, vì vậy bạn có thể muốn sử dụng chức năng này trong Expert Advisor
của mình. Xem Phụ lục D để biết mã dừng bán hoàn chỉnh. Dừng hòa vốn Bạn cũng có
thể sử dụng phương pháp này để áp dụng điều chỉnh dừng hòa vốn cho các lệnh của
mình. Điểm dừng hòa vốn điều chỉnh mức dừng lỗ bằng với giá mở lệnh sau khi đạt
được mức lợi nhuận nhất định. Điểm dừng hòa vốn độc lập với các chức năng dừng lỗ
và dừng treo ban đầu của bạn. 90 www.ZTCprep.com Quản lý đơn hàng Đây là biến
bên ngoài cho việc thiết lập lợi nhuận hòa vốn của chúng tôi. Lợi nhuận tối thiểu được
chỉ định bằng pip. bên ngoài gấp đôi BreakEvenProfit = 25; Mã này sẽ sửa đổi mức
dừng lỗ trên tất cả các lệnh thị trường mua để hòa vốn, khi lợi nhuận của lệnh tính
bằng pip bằng hoặc lớn hơn BreakEvenProfit. Chúng tôi sẽ không tạo chức năng cho
việc này nhưng bạn có thể làm như vậy nếu bạn cảm thấy nó hữu ích. for(int Bộ đếm =
0; Bộ đếm <= OrderTotal()-1; Bộ đếm++) { OrderSelect(Counter,SELECT_BY_POS);
RefreshRates(); gấp đôi PipsProfit = Giá thầu – OrderOpenPrice(); nhân đôi MinProfit =
BreakEvenProfit * PipPoint(OrderSymbol())); if(OrderMagicNumber() == MagicNumber
&& OrderSymbol() == Symbol() && OrderType() == OP_BUY && PipsProfit >= MinProfit
&& OrderOpenPrice() != OrderStopLoss()) { bool BreakEven =
OrderModify(OrderTicket(),OrderOpenprice (), OrderOpenPrice(),OrderTakeProfit(),0);
if(BreakEven == false) { ErrorCode = GetLastError(); chuỗi ErrDesc =
ErrorDescription(ErrorCode); chuỗi ErrAlert = StringConcatenate("Mua hòa vốn - Lỗi",
ErrorCode,": ",ErrDesc); Cảnh báo(ErrAlert); string ErrLog = StringConcatenate("Bid:
",Bid,", Ask: ",Ask, ", Ticket: ",CloseTicket,", Stop: ",OrderStopLoss(),", Break: ", MinProfit);
In(ErrLog); } } } Chúng tôi trừ giá mở lệnh khỏi Giá thầu hiện tại để tính lợi nhuận hiện
tại tính bằng pip và lưu trữ số tiền này trong PipsProfit. Chúng tôi tính toán lợi nhuận
tối thiểu bằng pip và lưu trữ số tiền đó trong MinProfit. Nếu PipsProfit lớn hơn hoặc
bằng MinProfit thì chúng tôi sẽ sửa đổi mức dừng lỗ bằng với giá mở lệnh. Chúng tôi
cũng kiểm tra để đảm bảo rằng mức dừng lỗ chưa được đặt ở mức giá hòa vốn. Nếu
OrderOpenPrice() không bằng OrderStopLoss() thì chúng ta có thể tiếp tục. 91
www.ZTCprep.com CHƯƠNG TRÌNH Expert Advisor Cập nhật Expert Advisor Hãy sửa
đổi hàm start() của đường trung bình động cố vấn chéo chuyên gia của chúng ta để
phản ánh các hàm mới mà chúng ta đã tạo. Đầu tiên, chúng tôi sẽ kiểm tra xem có
lệnh mua nào được mở hay không trước khi mở thêm. Thay vì đóng một lệnh bán duy
nhất, chúng ta sẽ chỉ sử dụng chức năng đóng tất cả các lệnh bán. Phương pháp này
không yêu cầu chúng ta phải sử dụng phiếu đặt hàng. // Lệnh mua if(FastMA >
SlowMA && BuyTicket == 0 && BuyMarketCount(Symbol(),MagicNumber) == 0) {
if(SellMarketCount(Symbol(),MagicNumber) > 0) {
CloseAllSellOrders(Symbol(),MagicNumber, Trượt dốc); } BánTicket = 0; BuyTicket =
OpenBuyOrder(Symbol(),LotSize,UseSlippage,MagicNumber); } Chúng ta đã sử dụng
hàm BuyMarketCount() mà chúng ta đã xác định ở trang 84 để trả về số lượng lệnh
mua hiện đang mở. Chúng tôi sẽ tiếp tục kiểm tra MuaTicket để chỉ mở các lệnh
mua/bán xen kẽ. Hàm CloseAllSellOrders() đóng mọi lệnh bán đang mở. Trước tiên,
chúng tôi kiểm tra SellMarketCount() để xem liệu có lệnh bán nào cần đóng hay
không. Chức năng này không yêu cầu phiếu đặt hàng, không giống như hàm
CloseSellOrder() trong chương 4. Bạn nên sử dụng phương pháp này để đóng các đơn
đặt hàng ngược lại trong EA của mình vì nó mạnh mẽ hơn. Phần còn lại của mã đặt
lệnh mua giống như trước. Mã đặt lệnh bán tương ứng bên dưới: // Lệnh bán
if(FastMA < SlowMA && SellTicket == 0 && SellMarketCount(Symbol(),MagicNumber)
== 0) { if(BuyMarketCount(Symbol(),MagicNumber) > 0) {
CloseAllBuyOrders(Symbol(),MagicNumber,Slippage); } MuaVé = 0; SellTicket =
OpenSellOrder(Symbol(),LotSize,UseSlippage,MagicNumber); } 92 www.ZTCprep.com
Quản lý đơn hàng Tiếp theo, hãy thêm chức năng dừng cuối vào đơn hàng của chúng
ta. Chúng tôi sẽ thực hiện quy trình dừng treo sau khi đặt lệnh. Như trên, chúng ta sẽ
kiểm tra các lệnh mua hoặc bán đang mở trước khi gọi hàm Trailing Stop. Hãy thêm
các biến bên ngoài sau vào EA của chúng tôi: extern int TrailingStop = 50; extern int
Lợi nhuận tối thiểu = 50; Đây là mã để kiểm tra và sửa đổi các điểm dừng cuối. Lưu ý
rằng chúng tôi kiểm tra xem có mục nào cho cài đặt TrailingStop hay không. Nếu được
đặt thành 0 thì nó thực sự bị vô hiệu hóa: if(BuyMarketCount(Symbol(),MagicNumber)
> 0 && TrailingStop > 0) {
BuyTrailingStop(Symbol(),TrailingStop,MinimumProfit,MagicNumber); }
if(SellMarketCount(Symbol(),MagicNumber) > 0 && TrailingStop > 0) {
SellTrailingStop(Symbol(),TrailingStop,MinimumProfit,MagicNumber); } Bạn có thể
xem những thay đổi này trong ngữ cảnh trong Phụ lục C. 93 www.ZTCprep.com LẬP
TRÌNH EXPERT AdvisorS Chương 6 Các điều kiện và chỉ báo đơn hàng Chúng tôi đã
dành vài chương trước để tạo ra các chức năng thực hiện cơ chế đặt hàng phổ biến
đối với mọi chuyên gia cố vấn . Các chức năng này nhằm mục đích sử dụng trong
nhiều tình huống giao dịch khác nhau và phải có thể tái sử dụng và linh hoạt nhất có
thể. Điều này cho phép chúng tôi tập trung vào việc mã hóa các điều kiện giao dịch
duy nhất cho hệ thống giao dịch của mình. Đây là nơi mà hầu hết công việc của bạn sẽ
tập trung - nhờ cố vấn chuyên môn giao dịch hệ thống của bạn một cách chính xác
nhất có thể. Chúng ta sẽ cần xác định các điều kiện chính xác để mở và đóng lệnh,
cũng như xác định mức dừng lỗ và chốt lãi. Hầu hết mọi hệ thống giao dịch đều sử
dụng dữ liệu về giá và/hoặc chỉ báo. Hãy xem xét những cách chúng tôi có thể truy
cập và sử dụng thông tin này trong chuyên gia cố vấn của chúng tôi. Dữ liệu Giá Cùng
với giá Mua hoặc Bán hiện tại (mà chúng ta đã đề cập trong các chương trước), bạn
có thể cần sử dụng dữ liệu giá thanh, cụ thể là giá cao, thấp, mở hoặc đóng của một
thanh cụ thể. Đối với biểu đồ hiện tại, bạn có thể sử dụng các mảng chuỗi được xác
định trước Cao[], Thấp[], Mở[] và Đóng[]. Mảng là một biến chứa nhiều giá trị. Bạn duyệt
qua các giá trị bằng cách thay đổi chỉ mục được chứa trong dấu ngoặc vuông. Ví dụ:
Open[0] là giá mở của thanh hiện tại. 0 là chỉ số và bằng cách thay đổi nó, chúng ta có
thể nhận được giá mở của các thanh khác. Thanh trước thanh hiện tại sẽ có chỉ số là
1, v.v. Chúng ta sẽ thường xuyên sử dụng giá trị của thanh hiện tại hoặc thanh trước
đó. Nếu bạn cần giá trị cao, thấp, mở hoặc đóng cho một biểu tượng không phải biểu
đồ hiện tại hoặc nếu bạn cần dữ liệu giá trong một khoảng thời gian khác với khoảng
thời gian biểu đồ hiện tại, bạn có thể sử dụng các hàm iHigh(), iLow(), iOpen() và
iClose(). Đây là cú pháp của các hàm này, sử dụng iClose() làm ví dụ: double
iClose(string Symbol, int Period, int Shift) • Symbol – Ký hiệu của cặp tiền tệ sẽ sử
dụng. • Khoảng thời gian – Khoảng thời gian của biểu đồ sẽ sử dụng, tính bằng phút. •
Shift – Sự dịch chuyển ngược so với thanh hiện tại. 94 www.ZTCprep.com Điều kiện và
Chỉ báo Lệnh Hãy sử dụng iClose() để biết giá đóng cửa cho một khoảng thời gian
biểu đồ khác. Ví dụ: chúng tôi đang sử dụng biểu đồ 1 giờ nhưng chúng tôi muốn kiểm
tra giá đóng của thanh trước đó trên biểu đồ 4 giờ: double H4Close =
iClose(NULL,PERIOD_H4,1); NULL đề cập đến biểu tượng biểu đồ hiện tại. PERIOD_H4
là hằng số nguyên đề cập đến khoảng thời gian của biểu đồ H4. 1 là sự thay đổi của
chúng tôi, là thanh trước thanh hiện tại. Hãy sử dụng một ví dụ khác trả về giá đóng
của thanh hiện tại trên biểu đồ khác: double GBPClose = iClose(GBPUSD,0,0); GBPUSD
là biểu tượng mà chúng tôi đang sử dụng. Chúng tôi đã chỉ định 0 là khoảng thời gian
của mình, vì vậy khoảng thời gian biểu đồ mà chúng tôi đang kiểm tra trên GBPUSD sẽ
giống với biểu đồ hiện tại của chúng tôi. Sự thay đổi là 0, là thanh hiện tại. Bạn có thể
sử dụng toán tử vòng lặp như for hoặc while để tăng tham số Shift và duyệt qua lịch
sử biểu đồ. Vòng lặp for này truy xuất giá đóng cửa của mỗi thanh trong số mười
thanh cuối cùng và in nó vào nhật ký: for(int Count = 0; Count <= 9; Count++) { double
CloseShift = iClose(NULL,0,Count); In(Đếm+” “+CloseShift); } Chỉ báo Phần lớn các hệ
thống giao dịch sử dụng chỉ báo để xác định tín hiệu giao dịch. MetaTrader bao gồm
hơn 20 chỉ báo phổ biến, bao gồm đường trung bình động, MACD, RSI và ngẫu nhiên.
MQL có các chức năng tích hợp cho các chỉ số chứng khoán. Bạn cũng có thể sử dụng
các chỉ báo tùy chỉnh trong chuyên gia cố vấn của mình. Các chỉ báo Xu hướng Chúng
ta hãy xem xét chỉ báo mà chúng tôi đã sử dụng trong suốt cuốn sách này: đường
trung bình động. Đường trung bình động là một chỉ báo xu hướng. Nó cho biết giá đã
tăng hay giảm trong khoảng thời gian chỉ báo. Đường trung bình động bao gồm một
đường duy nhất được vẽ trên biểu đồ hiển thị giá trung bình trên số x thanh cuối cùng.
Đây là cú pháp của hàm trung bình động: 95 www.ZTCprep.com EXPERT ADVISOR
PROGRAMMING double iMA(string Symbol, int Timeframe, int MAPeriod, int MAShift,
int MAMethod, int MAprice, int Shift) • Symbol – Ký hiệu của biểu đồ để áp dụng
đường trung bình động. • Khung thời gian – Khoảng thời gian áp dụng đường trung
bình động trên biểu đồ. Mọi chức năng chỉ báo trong MQL đều bắt đầu với hai tham số
này. Sau đây là các thông số cụ thể của chỉ báo. Chúng tương ứng với nội dung của
tab Tham số trong thuộc tính Chỉ báo. • MAPeriod – Khoảng thời gian nhìn lại của
đường trung bình động. Hầu hết mọi chỉ báo đều có ít nhất một tham số chu kỳ. Hầu
hết các chỉ báo đều được tính toán bằng cách sử dụng chuỗi giá lấy từ các thanh
trước đó. Ví dụ: cài đặt khoảng thời gian là 10 có nghĩa là chỉ báo sử dụng dữ liệu giá
từ mười thanh cuối cùng để tính giá trị chỉ báo. • MAShift – Sự dịch chuyển về phía
trước của đường trung bình động, tính bằng thanh. Điều này khác với tham số Shift
bên dưới. • MAMethod – Phương pháp tính đường trung bình động. Các lựa chọn bao
gồm đơn giản, hàm mũ, làm mịn hoặc trọng số tuyến tính. Bất kỳ chỉ báo nào sử dụng
đường trung bình động đều có thể cung cấp cho bạn tùy chọn để chọn phương pháp
tính MA. Chúng ta sẽ nói về các phương pháp trung bình động ở phần sau của chương
này. • MAPrice – Mảng giá sử dụng khi tính toán đường trung bình động. Đây có thể là
giá đóng, mở, cao, thấp hoặc một số loại trung bình; chẳng hạn như giá trung bình, giá
điển hình hoặc giá có trọng số. Chúng ta sẽ thảo luận về hằng số giá được áp dụng ở
phần sau của chương này. • Shift – Dịch chuyển lùi của thanh để trả về phép tính.
Tham số Shift là tham số cuối cùng trong bất kỳ hàm chỉ báo nào. Đây là chỉ mục của
thanh để trả về giá trị chỉ báo. Giá trị 0 trả về giá trị chỉ báo cho thanh hiện tại. Giá trị 3
sẽ trả về giá trị chỉ báo từ 3 thanh trước. Đường trung bình động và các chỉ báo tương
tự được vẽ trực tiếp trên biểu đồ. Bạn có thể tạo điều kiện giao dịch dựa trên mối quan
hệ giữa các chỉ báo và giá cả. Đường chéo trung bình động của chúng tôi là một ví dụ
về mối quan hệ giá giữa hai chỉ báo. Khi giá của một chỉ báo này lớn hơn giá của chỉ
báo kia, tín hiệu mua hoặc bán sẽ được tạo ra. 96 www.ZTCprep.com Điều kiện đặt
hàng và chỉ báo Bạn cũng có thể tạo tín hiệu giao dịch khi giá hiện tại vượt lên trên
hoặc xuống dưới đường chỉ báo. Ví dụ: chỉ báo Dải bollinger có thể được sử dụng để
tạo tín hiệu giao dịch dựa trên vị trí của giá so với dải trên và dải dưới. Bộ dao động
Loại chỉ báo chính khác là bộ dao động. Các chỉ báo dao động được vẽ trong một cửa
sổ riêng biệt và như tên gọi của chúng, chúng dao động giữa mức giá cao nhất và mức
giá thấp nhất. Các bộ dao động có tâm quanh một trục trung hòa (thường là 0) hoặc
chúng bị giới hạn bởi một cực trên hoặc cực dưới (chẳng hạn như 0 và 100). Ví dụ về
các bộ dao động bao gồm động lượng, ngẫu nhiên và RSI. Các chỉ báo dao động cho
biết mức quá mua và quá bán. Mặc dù chúng có thể được sử dụng như một chỉ báo về
xu hướng nhưng chúng thường được sử dụng để xác định các khu vực đang chờ đảo
chiều. Chúng được sử dụng để tạo ra tín hiệu giao dịch ngược xu hướng. Chúng ta hãy
nhìn vào một bộ dao động phổ biến, ngẫu nhiên. Stochastics bao gồm hai đường,
đường ngẫu nhiên (còn gọi là đường %K) và đường tín hiệu (đường %D). Stochastic
dao động trong khoảng từ 0 đến 100. Khi stochastic trên 70, nó được cho là quá mua
và đang chờ khả năng đảo chiều. Nếu nó dưới 30, nó được cho là quá bán. Đây là cú
pháp cho chỉ báo ngẫu nhiên: double iStochastic(string Symbol, int Timeframe, int
KPeriod, int D Period, int Slowing, int MAMethod, int PriceField, int Mode, int Shift)
Chúng ta đã quen với hai tham số đầu tiên, Biểu tượng và khung thời gian. Hãy xem
xét các tham số cụ thể của chỉ báo: • KPeriod – Khoảng thời gian cho dòng %K. •
DPeriod – Khoảng thời gian cho dòng %D. • Slowing – Giá trị chậm lại của ngẫu nhiên.
Giá trị thấp hơn biểu thị ngẫu nhiên nhanh, trong khi giá trị cao hơn biểu thị tốc độ
chậm hơn. • MAMethod – Đường %D được áp dụng phương pháp trung bình động. Đây
là cài đặt tương tự như đối với đường trung bình động. Chúng ta sẽ sớm xem xét các
phương pháp trung bình động. • PriceField – Xác định dữ liệu giá được sử dụng cho
dòng %K. Đây là 0: Thấp/Cao hoặc 1: Đóng/Đóng. Giá trị bằng 1 có nhiều khả năng
ngẫu nhiên sẽ giao dịch ở mức cực trị của nó. • Chế độ – Xác định đường ngẫu nhiên
để tính toán – 1: đường %K hoặc 2: đường %D. 97 www.ZTCprep.com LẬP TRÌNH TƯ
VẤN CHUYÊN GIA Chúng ta hãy dành chút thời gian để nói về tham số Mode. Một số
chỉ báo vẽ nhiều đường trên biểu đồ. Stochastic có hai. Chúng ta sẽ cần gọi hàm
iStochastic() cho cả hai dòng %K và %D, như hiển thị bên dưới: double KLine =
iStochastic(NULL,0,KPeriod,DPeriod,Slowing,MAMethod,Price,0,0); double DLine =
iStochastic(NULL,0,KPeriod,DPeriod,Slowing,MAMethod,Price,1,0); Lưu ý rằng tham số
Chế độ là 0 cho dòng %K và 1 cho dòng %D. Chủ đề Tham chiếu MQL, Hằng số chuẩn
– Dòng chỉ báo liệt kê các hằng số nguyên hợp lệ cho các chỉ báo khác nhau sử dụng
tham số Chế độ. Bạn có thể tạo tín hiệu giao dịch dựa trên mối quan hệ giữa các
đường chỉ báo và các mức chỉ báo nhất định, chẳng hạn như mức quá mua và quá bán
lần lượt là 70 và 30. Bạn cũng có thể đánh giá các tín hiệu giao dịch dựa trên mối quan
hệ giữa các đường chỉ báo với nhau. Ví dụ: bạn có thể chỉ muốn mở lệnh mua khi dòng
%K nằm trên dòng %D. Dưới đây là một số điều kiện ví dụ: if(KLine < 70) // Mua nếu
ngẫu nhiên không bị mua quá mức if(KLine > DLine) // Mua nếu %K lớn hơn %D Các
hàm chỉ báo tích hợp có trong Tham chiếu MQL trong phần Kỹ thuật các chỉ số. Nếu
bạn muốn biết thêm thông tin về cách sử dụng hoặc phương pháp tính toán của chỉ
báo, hãy tham khảo phần phân tích kỹ thuật của trang web MQL tại
http://ta.mql4.com/. Chỉ báo tùy chỉnh Hàng trăm chỉ báo tùy chỉnh cho MetaTrader
có sẵn trực tuyến. Nếu bạn quyết định sử dụng một chỉ báo tùy chỉnh trong cố vấn
chuyên gia của mình, bạn sẽ phải thực hiện một số công việc khó khăn. Tốt nhất là
bạn nên có tệp mã nguồn .mq4 khi sử dụng chỉ báo tùy chỉnh. Mặc dù có thể sử dụng
chỉ báo tùy chỉnh mà không cần đến nó, nhưng việc có mã nguồn sẽ giúp việc tìm ra
chỉ mục bộ đệm cho tham số Chế độ trở nên dễ dàng hơn. MQL có chức năng tích hợp
để xử lý các chỉ báo tùy chỉnh – iCustom(). Đây là cú pháp: double iCustom(string
Symbol, int Timeframe, string IndicatorName, Indicator Parameters, int Mode, int
Shift); Bạn đã quen thuộc với Biểu tượng, Khung thời gian, Chế độ và Shift từ phần đầu
của chương này. Hãy bắt đầu với IndicatorName. Đây là tên của tệp chỉ báo, chính xác
như tên xuất hiện trong danh sách Chỉ báo Tùy chỉnh trong cửa sổ Điều hướng. Ví dụ:
"Đường hướng dốc" hoặc "super_signal". 98 www.ZTCprep.com Điều kiện đặt hàng và
chỉ báo Tham số chỉ báo là nơi chúng tôi chèn các tham số cho chỉ báo tùy chỉnh. Tab
Đầu vào trong cửa sổ Thuộc tính chỉ báo tùy chỉnh sẽ hiển thị các tham số cho chỉ báo
tùy chỉnh. Các biểu tượng bên trái của mỗi thông số sẽ cho biết loại dữ liệu. Nếu bạn
không có tệp .mq4 cho chỉ báo, bạn sẽ phải xác định các tham số chỉ báo từ hộp thoại
này. Hình 6.1 – Hộp thoại nhập chỉ báo tùy chỉnh Một cách dễ dàng hơn để tìm các
tham số là kiểm tra các biến bên ngoài ở đầu tệp mã nguồn chỉ báo. Tất cả các tham
số chỉ báo, loại dữ liệu và giá trị mặc định của chúng sẽ được liệt kê ở đây. Bạn chỉ cần
sao chép và dán mã này vào phần biến bên ngoài của chuyên gia cố vấn của mình.
Mỗi và mọi biến bên ngoài trong chỉ báo tùy chỉnh phải có tham số tương ứng trong
hàm iCustom() và chúng phải theo thứ tự xuất hiện trong chỉ báo. Bạn có thể sử dụng
hằng số cho các tham số không cần thay đổi (chẳng hạn như chuỗi thông tin hoặc cài
đặt không cần thiết). Dưới đây là ví dụ: Chỉ báo tùy chỉnh phổ biến Đường dốc hướng
có các biến bên ngoài này được liệt kê trong mã nguồn. Chúng tôi sẽ tạo các biến bên
ngoài cho các cài đặt này trong cố vấn chuyên gia của chúng tôi: //---- tham số đầu
vào extern int Period=80; phương thức int bên ngoài = 3; // MODE_SMA giá int bên

Ố Ấ
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.....

You might also like