function loadPage ()
return 'Hello MQL5!';
Làm chủ MQL5 từ người mới bắt đầu đến chuyên nghiệp (Phần II): Các kiểu dữ liệu cơ bản và cách sử dụng biến
Published:

Làm chủ MQL5 từ người mới bắt đầu đến chuyên nghiệp (Phần II): Các kiểu dữ liệu cơ bản và cách sử dụng biến

Làm chủ MQL5 từ người mới bắt đầu đến chuyên nghiệp (Phần II): Các kiểu dữ liệu cơ bản và cách sử dụng biến

Giới thiệu

Trong bài viết trước của tôi, chúng ta đã xem xét các chương trình chính được sử dụng bởi các lập trình viên MQL5 (và đi đến kết luận rằng IDE MetaEditor rất phù hợp với nhu cầu của người mới bắt đầu). Ngoài ra, chúng ta đã xem qua khái niệm về function và tạo một script đơn giản in một thông điệp vào nhật ký hệ thống. Những thông điệp này có thể được xem ở dưới cùng của cửa sổ terminal trong tab Experts.

Tôi xin nhắc lại rằng một function là mô tả của hành động.

Chúng ta chỉ sử dụng các hàm được định sẵn: OnStartPrint. Đối với hàm đầu tiên, chúng ta đã điền nội dung vào nó. Hàm thứ hai, hiển thị thông tin chúng ta cần, được sử dụng ở dạng đã hoàn thiện, và chúng ta chỉ truyền các tham số cho nó. Lập trình viên có thể tạo các hàm tùy chỉnh của riêng mình để giải quyết các nhiệm vụ cụ thể.

Mỗi hàm bao gồm các bước-hành động rất cơ bản, được gọi là statements. Những hành động này khá đơn giản: so sánh hai số, lặp lại một đoạn mã nhiều lần, nối hai đoạn văn bản lại với nhau, gọi một hàm khác, v.v. Không có quá nhiều hành động như vậy. Chúng ta sẽ xem xét một số câu lệnh trong bài viết này.

Một chuỗi các câu lệnh tạo thành một algorithm.

Một algorithm bao gồm các hướng dẫn rõ ràng và dễ hiểu cho người thực hiện (trong trường hợp này là máy tính) để thực hiện các hành động nhất định dẫn đến việc giải quyết một nhiệm vụ cụ thể, mang tính toàn cục hơn. Có rất nhiều thuật toán, vì một vấn đề, theo quy luật, có thể được giải quyết theo nhiều cách khác nhau.

Điều này đặc biệt liên quan đến giao dịch. Việc vào một giao dịch, thoát khỏi giao dịch, in các nhật ký tương ứng và các hành động khác có thể được thực hiện theo nhiều cách khác nhau. Chúng ta cần giải thích một cách tuyệt đối rõ ràng cho máy tính chính xác những gì bạn (hoặc khách hàng của bạn) muốn trong trường hợp cụ thể này.

Có thể nói rằng các thuật toán phức tạp hơn bao gồm các thuật toán đơn giản hơn, và mỗi thuật toán được thực hiện bởi một hàm nào đó, thực hiện một số hành động. Những hành động này được áp dụng cho data. Dữ liệu này có thể là giá Ask và Bid hoặc khối lượng giao dịch đến vài lần mỗi giây. Các ví dụ khác về dữ liệu bao gồm các điểm trên màn hình mà giữa chúng bạn có thể vẽ một đường thẳng hoặc đường cong. Nó cũng có thể là âm thanh cần được phát khi một giao dịch được thực hiện. Dữ liệu có thể là văn bản, chẳng hạn như danh sách báo giá cho một khoảng thời gian nhất định. Có thể có rất nhiều ví dụ khác nhau. Tôi hy vọng ý tưởng đã rõ ràng.

Bây giờ chúng ta đến điểm mà dữ liệu này phải được lưu trữ ở đâu đó.

Hôm nay chúng ta sẽ nói về cách dữ liệu được lưu trữ trong RAM. Dữ liệu có thể được lưu trữ trong bộ nhớ dưới dạng variables hoặc constants.

Sự khác biệt rất rõ ràng:

  • Variables có thể thay đổi, tức là chương trình có quyền ghi đè dữ liệu như vậy.
  • Constants giữ nguyên không đổi trong suốt thời gian tồn tại của chương trình, và nếu lập trình viên cố gắng ghi đè giá trị của chúng, một lỗi biên dịch sẽ được trả về.

Ngoài ra, ý nghĩa của chúng hoàn toàn tương tự: đây là một khu vực nhất định của RAM lưu trữ dữ liệu, không phải lệnh của bộ xử lý. Thông thường, mọi người đặt tên có ý nghĩa cho các khu vực bộ nhớ này để hiểu chúng được sử dụng để làm gì.

Trình biên dịch sẽ xóa các tên này, nhưng nếu bạn có quyền truy cập vào mã nguồn (tệp văn bản), bạn luôn có thể hiểu mục đích của biến dựa trên tên của nó. Tất nhiên, với điều kiện chúng được mô tả chính xác.

Hằng số có thể không có tên trong một số trường hợp. Lập trình viên chỉ cần viết chính xác những gì cần được xử lý (ví dụ, các chuỗi mà chúng ta đã truyền cho hàm Print). Những hằng số không tên như vậy được gọi là literals.

Trong bài viết này, chúng ta sẽ xem xét kỹ hơn các kiểu dữ liệu cơ bản, cách mô tả biến và hằng số, và các câu lệnh cơ bản mà lập trình viên có thể sử dụng để tạo thuật toán. Điều này lần lượt sẽ cho phép bạn tạo ra các chương trình hữu ích hơn là chỉ “Hello, World”.

Mã cơ bản để kiểm tra tất cả các biểu thức từ bài viết

Trong bài viết trước, chúng ta đã tạo một chương trình đơn giản: một script in dữ liệu trong tab “Experts” ở bảng dưới cùng của terminal. Đây là nó:

//+------------------------------------------------------------------+
//|                                                   HelloWorld.mq5 |
//|                                       Oleg Fedorov (aka certain) |
//|                                   mailto:[email protected] |
//+------------------------------------------------------------------+
#property copyright "Oleg Fedorov (aka certain)"
#property link      "mailto:[email protected]"
#property version   "1.00"
//#property script_show_inputs

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
  Print("Hello, MQL5 world!");
  }

//+------------------------------------------------------------------+

Ví dụ 1. Toàn bộ văn bản của script đơn giản nhất

Hôm nay chúng ta sẽ làm việc với mã này: chúng ta sẽ sửa đổi nó bằng cách chèn các dòng từ các ví dụ vào trong dấu ngoặc nhọn (trừ khi phần giải thích cho các ví dụ chỉ rõ một vị trí khác để chèn).

Literals

Hãy xem cách chúng ta có thể in dữ liệu.

Chúng ta sẽ bắt đầu với chuỗi.

Một string literal được bao quanh bởi dấu ngoặc kép ". Tuy nhiên, không phải tất cả các ký tự đều có thể được hiển thị một cách đơn giản. Một số trong chúng có ý nghĩa đặc biệt trong ngôn ngữ (cùng dấu ngoặc kép), một số không được hiển thị chút nào, chẳng hạn như ký tự xuống dòng.

Có một quy ước đặc biệt cho các ký tự như vậy: chúng được viết bằng dấu gạch chéo ngược:

Print(
    "Symbol \" can't be placed",
    " without (\\).\n  And we can carrying over the string now."
  );

Ví dụ 2. Sử dụng dấu gạch chéo ngược để in các ký tự đặc biệt

Trong ví dụ 2, dấu ngoặc kép mô tả các chuỗi đầu ra được làm nổi bật bằng màu xanh lá cây, và các ký tự đặc biệt được làm nổi bật bằng màu vàng. Vì vậy, \n biểu thị một lần xuống dòng.

Ví dụ về đầu ra chuỗi literal

Hình 1. Ví dụ về đầu ra chuỗi literal

Cũng xin lưu ý đến tính năng sau. Trong MQL5, các chuỗi rất dài có thể được chia ở bất kỳ vị trí nào (bằng cách đặt mỗi phần trong dấu ngoặc kép). Không cần thêm bất kỳ hành động nào khác để hợp nhất các dòng này. Nếu trình biên dịch gặp hai chuỗi literal liên tiếp (như trong ví dụ), nó sẽ tự động hợp nhất các chuỗi thành một chuỗi lớn, sau đó tìm kiếm các ký tự đặc biệt trong đó.

Bảng đầy đủ các ký tự đặc biệt, cũng như mô tả chi tiết hơn về hằng số ký tự, có thể được tìm thấy trong Trợ giúp chính thức.

Kiểu được sử dụng thường xuyên thứ hai là số.

Integer numbers có dạng phổ biến:

Print(5);

Ví dụ 3. Đầu ra số nguyên

Decimals cũng khá dễ xuất ra. Dấu phân cách giữa phần nguyên và phần thập phân là dấu chấm:

Print(5.5);

Ví dụ 4. Đầu ra số thập phân

Đối với các số rất lớn hoặc rất nhỏ, bạn có thể sử dụng ký hiệu điểm nổi (đôi khi được gọi là dạng lũy thừa):

Print(5.5e3);
Print(5.5e-3);

Ví dụ 5. Sử dụng literal điểm nổi

Kết quả của script sử dụng tất cả các dạng số này được hiển thị trong Hình 2.

In số bằng hàm Print

Hình 2. Kết quả in số bằng hàm Print

Lưu ý cách hàm đã chuyển đổi dữ liệu được truyền cho nó trong hai dòng cuối. Điều này cho thấy dữ liệu đã được nhận diện là số và được xử lý phù hợp.

Đôi khi lập trình viên phải làm việc với hexadecimal numbers. Người mới bắt đầu hiếm khi cần kiểu này, nhưng đôi khi nó có thể hữu ích, ví dụ, để mô tả màu sắc. Số thập lục phân bao gồm các số (0..9) và/hoặc chữ cái (a..f).

Để chương trình hiểu rằng đây là một literal thập lục phân, bạn cần thêm “0x” vào đầu số (zero và chữ x).

Cách biểu diễn không phân biệt chữ hoa chữ thường.

Print(0xAF35);

Ví dụ 6. Sử dụng số thập lục phân

Kết quả đầu ra số thập lục phân

Hình 3. Kết quả đầu ra số thập lục phân

Kết quả của script được hiển thị trong Hình 3. Số đã được chuyển đổi (thành số nguyên thông thường), điều đó có nghĩa là máy tính đã hiểu nó chính xác như cần thiết.

Lập trình viên MQL5 rất thường xuyên phải sử dụng ngày tháng.

Để ghi chú đầy đủ về date and time, bắt đầu bằng chữ cái in hoa D, sau đó đặt dấu nháy đơn < ' >, viết ngày mong muốn bằng dấu chấm hoặc dấu gạch chéo, nếu cần, chỉ định thời gian sau một khoảng trắng, phân tách phút và giây bằng dấu hai chấm, và thêm một dấu nháy đơn khác:

  Print(D'24/12/23');
  Print(D'24.12');
  Print(D'24.12.2023 7:55:34');

Ví dụ 7. Sử dụng ngày và giờ

Lưu ý rằng trong trường hợp dòng thứ hai, chúng ta sẽ nhận được cảnh báo từ trình biên dịch, nói rằng ngày không đầy đủ. Tuy nhiên, tệp sẽ được biên dịch, và cả ba dòng sẽ hoạt động chính xác:

Cảnh báo về literal ngày không đầy đủ

Hình 4. Cảnh báo (MetaEditor) về literal ngày không đầy đủ

Kết quả của script xuất ngày

Hình 5. Kết quả của script xuất ngày (terminal)

Xin lưu ý rằng khi biên dịch dòng đầu tiên, thời gian bắt đầu của ngày đã được thay thế, và khi biên dịch dòng thứ hai, thời gian biên dịch đã được thay thế. Vì định dạng đã được chuyển đổi đúng cách, chúng ta có thể kết luận rằng chương trình đã hiểu nó chính xác.

Trên bất kỳ literal nào, bạn có thể thực hiện bất kỳ hành động nào được phép cho kiểu dữ liệu đó.

Ví dụ, bạn có thể so sánh số, thực hiện các phép toán số học, và truyền chúng làm tham số cho các hàm.

Chuỗi có thể được cộng (chúng được nối lại với nhau) nhưng không thể trừ.

Hãy xem xét ví dụ sau:

Print( "This will be calculated: "+4+9 );
Print( "This will be calculated: "+(4+9) );

Ví dụ 8. Sử dụng dấu ngoặc khi viết biểu thức.

Cảnh báo trình biên dịch về sử dụng số trong biểu thức chuỗi

Hình 6. Cảnh báo trình biên dịch về sử dụng số trong biểu thức chuỗi

Kết quả tính toán

Hình 7. Hàm xuất kết quả tính toán

Nơi không có dấu ngoặc, các số chỉ đơn giản là “dán” vào văn bản. Khi chúng ta sử dụng dấu ngoặc, mọi thứ được tính toán chính xác. Đây là một lỗi khá khó phát hiện, đó là lý do tại sao trình biên dịch cảnh báo chúng ta ngay lập tức. Có các hàm đặc biệt để chuyển đổi số thành chuỗi một cách rõ ràng. Nhưng hiện tại, chỉ cần nhớ: dấu ngoặc rất quan trọng.

Xác định hằng số bằng chỉ thị tiền xử lý #define

Nếu bạn không muốn bị nhầm lẫn trong mã của mình và muốn hiểu mục đích của dữ liệu được sử dụng trong chương trình, bạn nên luôn cố gắng đặt tên có ý nghĩa cho tất cả các hằng số. Để làm điều này, chỉ thị tiền xử lý #define thường được sử dụng:

#define name value

Ví dụ 9. Chỉ thị #define

Tôi xin nhắc lại rằng ngôn ngữ tiền xử lý giống như một “ngôn ngữ trong ngôn ngữ”, và ngôn ngữ này mô tả các hành động trước khi bắt đầu biên dịch.

Theo quy tắc, tiền xử lý nhằm thay thế một số đoạn mã bằng các đoạn khác. Ví dụ, chỉ thị #define trong lần đầu tiên hướng dẫn trình biên dịch thay thế name bằng value trong toàn bộ mã của chúng ta và chỉ sau đó bắt đầu bất kỳ kiểm tra cú pháp nào.

Ví dụ, chúng ta có thể viết:

  #define MY_SUPER_CONSTANT 354
  Print(MY_SUPER_CONSTANT);

Ví dụ 10. Chỉ thị #define

Chương trình xuất giá trị, không phải tên

Hình 8. Chương trình xuất giá trị của hằng số, không phải tên

Khi thực thi, chương trình sẽ hiển thị chính xác số 354, chứ không phải tên.

Lưu ý rằng literal mô tả số không được theo sau bởi dấu chấm phẩy.

Khi khai báo hằng số bằng chỉ thị tiền xử lý, không cần dấu chấm phẩy.

Nếu chúng ta đã thêm dấu chấm phẩy, tiền xử lý sẽ chèn dấu này cùng với số, bên trong dấu ngoặc của Print, và chúng ta sẽ nhận được thông báo lỗi biên dịch.

Vì vậy, hãy nhớ rằng chúng ta đặt tên cho một hằng số và trong biểu thức chúng ta sử dụng tên của nó, không phải giá trị.

Nhân tiện, tên rất hữu ích khi cùng một hằng số được sử dụng nhiều lần ở các nơi khác nhau trong chương trình hoặc khi nhiều hằng số có cùng giá trị. Nếu giá trị của một hằng số thay đổi, việc thay đổi nó ở một nơi duy nhất, thường ở ngay đầu tài liệu hoặc thậm chí trong một tệp riêng, sẽ dễ dàng hơn nhiều so với việc phải tìm kiếm và thay đổi ở nhiều nơi.

Mô tả biến

Hãy nhớ: nếu bạn mong đợi dữ liệu trong bộ nhớ sẽ thay đổi trong quá trình làm việc, hãy sử dụng biến.

Việc mô tả biến cũng khá đơn giản. Chỉ cần viết ra chính xác những gì bạn muốn lưu trữ:

type variable_name;

Ví dụ 11. Mẫu khai báo biến

Mục này có nghĩa là trình biên dịch phải cấp phát một lượng bộ nhớ nhất định cho dữ liệu. Chúng ta sẽ xem xét các kiểu và kích thước chi tiết sau.

Dữ liệu này giờ đây có thể được truy cập bằng tên (variable_name).

Quy ước định danh

Tên của biến (name, thường được gọi là identifier), cũng như bất kỳ tên nào bạn tạo, phải

  • mang tính thông tin (“chartNumber” tốt hơn “sss”),
  • bao gồm các chữ cái của bảng chữ cái Latinh, số và dấu gạch dưới (_).

Tên không được

Tên phân biệt chữ hoa chữ thường. Ví dụ, myVariable và MyVariable là hai tên khác nhau. (Rõ ràng, việc sử dụng cả hai tên này trong cùng một tệp là rất không được khuyến nghị).

Nếu bạn vô tình nhầm lẫn chữ hoa chữ thường ở đâu đó trong văn bản chương trình, trình biên dịch sẽ đưa ra thông báo lỗi: “Biến chưa được khai báo” để bạn có thể dễ dàng sửa chữa. Nhưng nếu bạn mô tả cả hai biến theo tất cả các quy tắc nhưng sử dụng các tên tương tự chỉ khác nhau về chữ hoa chữ thường, sẽ quá dễ bị nhầm lẫn.

Ngoài ra, không có hạn chế nào. Bạn thậm chí có thể đặt tên biến của mình theo một hàm tích hợp nếu bạn muốn (hy vọng là không).

Toán tử gán

Để ghi dữ liệu vào một biến, sử dụng thao tác assignments. Đôi khi điều này được thực hiện tại thời điểm khai báo – trường hợp này được coi là initialization:

// Initialization (at creation)
int counter = 0;
// Normal assignment
counter = 10;

Ví dụ 12. Gán đơn giản

Từ int chỉ ra rằng biến chỉ có thể chứa dữ liệu kiểu số nguyên.

Dấu ”=” trong ví dụ này có nghĩa là toán tử gán operator. Trong trường hợp này, một số nguyên được ghi vào ô có tên ‘counter’.

Tất cả dữ liệu có trong ô trước khi gán sẽ bị mất.

Dữ liệu trong biến này có thể được sử dụng ở bất kỳ đâu trong chương trình bằng tên, ví dụ, bạn có thể truyền chúng làm tham số cho bất kỳ hàm nào hoặc sử dụng trong một biểu thức (các ví dụ sẽ có sau trong bài viết).

Thao tác gán - các đặc điểm cụ thể

Việc gán có thể rất đơn giản, như đã mô tả trong phần trước. Nhưng nếu bạn sử dụng thao tác này trong các biểu thức, thì các ghi chú sau sẽ giúp bạn sử dụng nó hiệu quả hơn.

  • Thao tác gán có độ ưu tiên thấp nhất, vì vậy nó được thực thi từ phải sang trái. Vì vậy, có một biểu thức ở bên phải, và ở bên trái là một biến nơi dữ liệu được ghi. Đầu tiên, biểu thức là:
a = b + c;

Ví dụ 13. Gán có độ ưu tiên thấp nhất.

Biểu thức trên sẽ cộng b và c trước, sau đó ghi kết quả của biểu thức này vào biến a.

  • Là hệ quả của ghi chú trước, giá trị của một biến có thể được sử dụng trong một biểu thức, và sau đó kết quả có thể được ghi vào cùng biến đó:
a = a — c;

Ví dụ 14. Bạn có thể sử dụng giá trị trước đó của một biến trong một biểu thức

  • Trong MQL5, các biểu thức mà cùng một biến xuất hiện một lần ở bên phải và bên trái (như trong ví dụ trên) có thể được đơn giản hóa, bằng cách di chuyển dấu biểu thức sang phía bên trái của thao tác:
a -= c;

Ví dụ 15. Sử dụng gán ngắn gọn

Cách sử dụng ngắn gọn như vậy hợp lệ cho bất kỳ toán tử nhị phân nào (sử dụng hai giá trị, như tổng hoặc nhân): nhân, chia, dịch chuyển, v.v. Tuy nhiên, lưu ý rằng biến trong biểu thức này nên dễ dàng được tách biệt.

Ví dụ, đối với biểu thức a = a*(1+1/a), thủ thuật này sẽ không còn hoạt động nếu bạn không mở dấu ngoặc, nhưng đối với a = a*(b+c) thì dễ dàng: a *= b+c.

  • Nếu bạn cần tăng hoặc giảm một số nguyên đi 1, không cần sử dụng gán. Thay vào đó, có thể sử dụng các thao tác incrementdecrement:
a++; // Increment. Tăng a lên 1
b--; // Decrement. Giảm b đi 1

Ví dụ 16. Tăng và Giảm

Các thao tác này là đơn phân, tức là chúng chỉ yêu cầu một biến để hoạt động. Các thao tác như vậy có hai dạng ký hiệu: tiền tố và hậu tố.

Khi sử dụng ở dạng tiền tố, hành động sẽ được thực hiện trước, sau đó kết quả sẽ được sử dụng trong biểu thức.

Khi sử dụng ở dạng hậu tố, giá trị cũ của biến sẽ được sử dụng trước, sau đó biến sẽ thay đổi đi 1:

int a = 1;
Print (++a); // 2, và a == 2
Print (a++); // 2, nhưng a == 3

Ví dụ 17. Dạng tiền tố và hậu tố của tăng (giảm được sử dụng hoàn toàn tương tự)

  • Bạn có thể sử dụng nhiều toán tử gán liên tiếp với “xếp tầng”. Trình tự hành động từ phải sang trái được giữ nguyên.
int a=1, c=3;
a = c = a+c; // đầu tiên a+c (4), sau đó c = 4, rồi a = c (tức là a = 4)

Ví dụ 18. Gán “xếp tầng”

Các kiểu dữ liệu cơ bản

Có tương đối nhiều kiểu dữ liệu.

Một số trong chúng là “đơn giản” (hoặc “cơ bản”). Đây là các kiểu như chuỗi, số, ngày tháng, màu sắc, v.v. Các kiểu khác là “phức tạp”; nhà phát triển chương trình MQL5 tạo ra các kiểu như vậy. Theo quy tắc, các kiểu dữ liệu “phức tạp” là sự kết hợp của các kiểu đơn giản, khi nhiều biến được kết hợp thành một khối để tiện lợi hơn.

Bài viết này sẽ chỉ đề cập đến các kiểu cơ bản. Chúng ta sẽ nói về các kiểu phức tạp trong phần tiếp theo.

Integer types

Số nguyên là cách chính mà máy tính “suy nghĩ”.

Các phép toán số nguyên trên máy tính đơn giản và nhanh chóng. Nhưng nếu kết quả của phép toán vượt quá một phạm vi giá trị nhất định, điều này có thể dẫn đến mất dữ liệu.

Điểm quan trọng thứ hai: số nguyên có thể là “có dấu” hoặc “không dấu”.

Nếu số là “không dấu”, thì bạn có thể sử dụng số từ 0 đến tối đa. Ví dụ, các số chiếm 1 byte có thể tăng từ 0 đến 2⁸-1 = 255, tổng cộng là 256 giá trị.

Nếu ai đó cố gắng ghi số “256” hoặc “-1” vào biến như vậy, sẽ xảy ra “tràn”. Trong trường hợp này, kết quả sẽ nằm trong cùng giới hạn [0..255], trong khi phần còn lại của dữ liệu sẽ bị mất. Đôi khi điều này có thể hữu ích, nhưng trong phần lớn các trường hợp, tốt hơn là sử dụng các phương pháp khác cho các biến đổi như vậy. Ví dụ, bạn có thể sử dụng toán tử lấy dư (sẽ nói sau trong bài viết này). Tốt hơn là sử dụng các biến của các kiểu sẽ chấp nhận tất cả dữ liệu của bạn mà không bị mất.

Tên của các kiểu bắt đầu từ 0 (và thực sự mô tả các số tự nhiên) được thêm chữ cái “u” phía trước (cho unsigned).

Số “có dấu” sử dụng cùng phạm vi giá trị, chia đôi. Nửa đầu lưu trữ số âm, nửa sau lưu trữ số dương. Do đó, cùng các số một byte sẽ đúng trong phạm vi [-128..127].

Bảng 1. Các kiểu dữ liệu số nguyên.

TênKích thước, byteGiá trị tối thiểuGiá trị tối đa
char1 (8 bit)-128127
uchar1 (8 bit)0255
short2 (16 bit)-32 76832 767
ushort2 (16 bit)065 535
int4 (32 bit)-2 147 483 6482 147 483 647
uint4 (32 bit)04 294 967 295
long8 (64 bit)-9 223 372 036 854 775 8089 223 372 036 854 775 807
ulong8 (64 bit)018 446 744 073 709 551 615

Trong thực tế, các kiểu được sử dụng phổ biến nhất là int (vì từ này dễ viết và dữ liệu của kiểu này khá lớn) và long (kích thước đủ cho phần lớn các nhiệm vụ, trình biên dịch có thể tối ưu hóa bytecode để đạt hiệu suất tốt nhất trên các máy tính hiện đại).

Tuy nhiên, các kiểu khác cũng hữu ích.

Boolean

Kiểu này được chỉ định bởi từ khóa bool, chiếm 1 byte bộ nhớ và chỉ có thể nhận hai giá trị: true hoặc false.

Nếu thực sự cần thiết, bất kỳ số nào cũng có thể được sử dụng làm dữ liệu logic. Ví dụ, nếu số bằng 0, thì nó là “false”; trong tất cả các trường hợp khác, nó là “true”. Tuy nhiên, bạn nên rất cẩn thận khi sử dụng số cho mục đích này.

Real numbers (aka floating point numbers)

Bảng 2. Các kiểu dữ liệu thực

TênKích thước, byteGiá trị dương tối thiểuGiá trị tối đa
float4 (32 bit)1.175494351e-383.402823466e+38
double8 (64 bit)2.2250738585072014e-3081.7976931348623158e+308

Kiểu ‘double’ chủ yếu được sử dụng trong thực tế hiện đại. Tôi đã không thấy kiểu ‘float’ trong mã của người khác trong một thời gian rất dài. Có lẽ nó được sử dụng để tương thích với các phiên bản cũ hơn. Mặc dù, trong các tập dữ liệu rất lớn, nó có thể hữu ích để tiết kiệm không gian bộ nhớ.

Số thực có thể biểu thị giá cả, số lượng tiền tệ, và các khái niệm hữu ích khác.

Chúng bao phủ một phạm vi giá trị lớn hơn nhiều so với số nguyên.

Tuy nhiên, máy tính không quá tiện lợi khi làm việc với các kiểu này. Thứ nhất, các phép toán với số thực chậm hơn một chút so với số nguyên. Thứ hai, do đặc thù định dạng, các phép tính hầu như luôn có sai số ở các chữ số cuối. Do đó, thay vì 1.0 trong một số phép toán, bạn có thể nhận được 1.00000001 và trong các trường hợp khác là 0.99999999.

Vì vậy, nếu cần so sánh hai số thực, bạn thường nên lấy hiệu của chúng và so sánh với một giá trị nhỏ nào đó, dù chắc chắn lớn hơn sai số. Đây là cách đáng tin cậy hơn.

Date and time

Kiểu này được biểu thị bằng từ datetime; nó chiếm 8 byte trong bộ nhớ.

Mỗi biến của kiểu này chứa số giây đã trôi qua kể từ ngày 1 tháng 1 năm 1970, cho đến ngày cần thiết. Vì vậy, nó là một số nguyên thông thường.

Ngày cuối cùng có thể là ngày 31 tháng 12 năm 3000. Điều này khá đủ cho cuộc đời chúng ta, vì vậy chúng ta không nên lo sợ “vấn đề năm 2000” (nếu bạn còn nhớ).

Có các hằng số được định sẵn đặc biệt:

  • __DATE__ — ngày biên dịch
  • __DATETIME__ — ngày và giờ biên dịch
  • Bạn có thể sử dụng biểu thức __DATETIME__ - __DATE__ - nó chỉ mô tả thời gian biên dịch, không có ngày.

Khi viết một literal, bạn có thể bỏ qua mọi thứ và viết giá trị là D'' (D và hai dấu nháy đơn). Ký hiệu này tương đương với __DATETIME__. Tuy nhiên, điều này làm giảm khả năng đọc của mã.

Colors

Màu sắc trong MQL5 được xác định bởi một kiểu riêng color. Khi mô tả một màu, bạn có thể sử dụng một literal:

  color myColor1=C'100,200,30';
  color myColor2=C'0xFF,0x00,0x5A';

Ví dụ 19. Mô tả màu bằng số thập phân hoặc thập lục phân

Bạn cũng có thể sử dụng các hằng số được định sẵn cho màu web. Tên hằng số màu bắt đầu bằng clr (ví dụ, clrBlue cho màu xanh). Danh sách đầy đủ các hằng số có thể được xem bằng cách nhập clr trong MetaEditor hoặc truy cập tài liệu chính thức.

Trong ví dụ 12, bạn có thể thấy rằng mỗi mô tả màu bao gồm ba đoạn. Mỗi đoạn như vậy mô tả cường độ của ánh sáng đỏ, xanh lá hoặc xanh dương của một điểm trên màn hình (Red, Green, Blue = RGB). Cùng nhau, chúng tạo ra toàn bộ sự đa dạng của màu sắc.

Bằng cách trộn đỏ và xanh lá, chúng ta nhận được tất cả các sắc thái của vàng-nâu-cam.

Đỏ và xanh dương tạo ra các tông màu tím-hồng.

Xanh lá và xanh dương cho ra các biến thể khác nhau của turquoise, cyan, v.v.

Trộn cả ba sắc thái theo tỷ lệ bằng nhau cho ra một loạt các sắc thái xám: từ đen - khi tất cả các thành phần đều “tắt”, tức là có cường độ 0, đến trắng nơi tất cả đều có cường độ tối đa 255 (0xFF).

Nếu tỷ lệ không bằng nhau, tức là cường độ của mỗi thành phần khác với các thành phần khác, tất cả các sắc thái khác trên màn hình sẽ được tạo ra. Xanh lá thường làm sáng màu tổng thể, trong khi xanh dương làm tối nó. Tất nhiên, nói chung, thành phần càng sáng thì càng nhạt (và màu tổng thể càng nhạt).

Tất cả các quy tắc này được minh họa trong Bảng 3, nơi tôi chỉ đơn giản là tô màu các ô bằng các màu có sẵn trong trình chỉnh sửa.

Trong thực tế, thường không cần biết giá trị số cho mỗi màu, trong khi bạn có thể chỉ cần chọn màu từ một bảng màu đặc biệt hoặc truyền một hằng số được định sẵn. Dù sao, tôi tin rằng việc hiểu cách mọi thứ hoạt động là hữu ích.

Dữ liệu màu chiếm 4 byte trong bộ nhớ, mặc dù chỉ 3 trong số đó được sử dụng. Điều này đã xảy ra trong lịch sử, và đây là một thỏa thuận chung cho bất kỳ chương trình nào có thể làm việc với mô hình mô tả màu này.

Bảng 3. Ví dụ về màu sắc

0, 0, 0156, 15, 15106, 0, 860, 49, 1100, 110, 4156, 37, 956, 37, 9
51, 51, 51191, 3, 3133, 2, 1080, 67, 1380, 137, 44243, 195, 087, 64, 30
102, 102, 102226, 8, 0160, 39, 1340, 87, 17455, 164, 44255, 221, 0117, 81, 26
153, 153, 153232, 87, 82177, 79, 15444, 114, 199119, 183, 83255, 235, 85143, 107, 50
204, 204, 204240, 134, 130193, 115, 17697, 147, 207177, 210, 143255, 242, 153179, 146, 93
255, 255, 255249, 204, 202232, 183, 215164, 192, 228216, 232, 194255, 246, 200222, 188, 133

Nếu muốn, bạn có thể làm việc với màu sắc giống như với các số nguyên thông thường.

Ví dụ, đây là mã:

  color a = C'255,0,0';
  color b = C'0,255,0';
  color d = a+b;
  Print(a," ",b," ",d);

Ví dụ 20. Sử dụng màu trong biểu thức số học

Điều này sẽ cho kết quả sau:

Kết quả sử dụng màu trong biểu thức số học

Hình 9. Kết quả sử dụng màu trong biểu thức số học

Enumerations

Kiểu cơ bản cuối cùng là liệt kê.

Có những tình huống khi, theo đặc thù của vấn đề, một biến chỉ nên nhận một số giá trị nhất định. Ví dụ, một xu hướng có thể là đi xuống, đi lên hoặc phẳng. Ngoài ra, chỉ có các loại lệnh Buy sau tồn tại: Buy (mua ở giá thị trường), Buy Stop (chờ đến khi giá đạt một mức nhất định để phá vỡ) và Buy Limit (chờ, kỳ vọng một sự bật lại). Các ngày trong tuần luôn giống nhau. Tôi nghĩ nguyên tắc đã rõ ràng.

Trong những trường hợp như vậy, chúng ta sử dụng enumerations.

Mô tả liệt kê bao gồm ba bước.

  1. Ở bước đầu tiên, bạn cần tạo danh sách và đặt tên cho nó theo cách nào đó. Tên kết quả là tên kiểu cho bất kỳ biến hoặc hàm nào. Sự khác biệt duy nhất giữa tên này và các kiểu được định sẵn là chúng ta tự nghĩ ra nó.
  2. Ở bước thứ hai, bạn tạo một biến của kiểu này.
  3. Và cuối cùng, ở bước thứ ba, bạn có thể sử dụng biến này.

Ví dụ 15 cho thấy cách mô tả và sử dụng một liệt kê. Để minh họa, tôi đã lấy mô tả hướng (DIRECTION), chỉ có thể sử dụng ba giá trị: “Upward”, “Downward” và “Aside”.

//--- Bước đầu tiên: tạo một danh sách mới (kiểu dữ liệu mới)
  enum ENUM_DIRECTION
   {
    Upward,
    Downward,
    Aside
   };

//--- Bước thứ hai: mô tả (và, nếu cần, khởi tạo) một biến của kiểu này
  ENUM_DIRECTION next=Upward;

//--- Bước thứ ba: sử dụng biến
  Print(next);

Ví dụ 21. Mô tả và sử dụng liệt kê

Thông thường, một danh sách liệt kê được tạo ngay ở đầu tệp, ngay sau các chỉ thị tiền xử lý. Trong trường hợp này, nó sẽ khả dụng cho tất cả các hàm của ứng dụng của chúng ta trên toàn cục.

Mặc dù bạn có thể mô tả nó cục bộ, bên trong một hàm nào đó. Khi đó liệt kê này sẽ không hiển thị từ các hàm khác. Thường thì điều này không có nhiều ý nghĩa, nhưng bạn không bao giờ biết mình sẽ đối mặt với nhiệm vụ gì.

Tên các phần tử liệt kê được chỉ định trong dấu ngoặc nhọn, phân tách bằng dấu phẩy.

Sau dấu ngoặc nhọn đóng của bất kỳ mô tả kiểu nào (bao gồm cả liệt kê) yêu cầu một dấu chấm phẩy. Điều này có thể không áp dụng cho các khối khác.

Các liệt kê được định sẵn trong ngôn ngữ có tên được viết bằng chữ in hoa, và các tên này bắt đầu bằng tiền tố ENUM_. Bạn có thể đặt tên cho liệt kê của mình bất cứ thứ gì bạn muốn (trong giới hạn), nhưng việc tuân thủ cùng tiêu chuẩn là một thực hành tốt.

Biểu diễn bên trong cho các liệt kê là một integer có dấu chiếm 4 byte trong bộ nhớ.

Nếu chúng ta cố gắng thực thi mã từ ví dụ 21, chúng ta sẽ thấy số 0. Điều này có nghĩa là khi chúng ta để MQL5 tự chọn số cho các phần tử liệt kê, nó bắt đầu từ số không.

Nhưng bạn có thể chỉ định một số khác, bạn chỉ cần đặt nó một cách rõ ràng:

//--- Bước đầu tiên: tạo một danh sách mới (kiểu dữ liệu mới)
  enum DIRECTION
   {
    Upward = 1,
    Downward = -1,
    Aside = 0
   };

Ví dụ 22. Đặt giá trị liệt kê một cách rõ ràng

Không cần chỉ định tất cả giá trị.

Nếu một số giá trị được chỉ định và một số không, MQL5 sẽ tự chọn số dựa trên thứ tự của các phần tử và số cao nhất cuối cùng. Điều này có nghĩa là nếu trong ví dụ 15 chúng ta đặt Upward = 1 và xóa tất cả các khởi tạo khác, thì Downward sẽ bằng 2, và Aside sẽ bằng 3. Bạn nên tự kiểm tra điều này.

Biểu thức và toán tử đơn giản

Khi chúng ta làm việc với dữ liệu, điều quan trọng là có thể so sánh nó, thực hiện các phép toán toán học, v.v. Các expressions khác nhau có thể được sử dụng cho các kiểu dữ liệu khác nhau.

Comparison operators

Các toán tử này có ý nghĩa cho tất cả các kiểu dữ liệu.

Kết quả so sánhlogic.

Các toán tử so sánh sau tồn tại:

  • Lớn hơn (>),
  • Nhỏ hơn (<),
  • Lớn hơn hoặc bằng (>=),
  • Nhỏ hơn hoặc bằng (<=),
  • Bằng (==),
  • Không bằng (!=)

Độ ưu tiên của tất cả các thao tác này là như nhau.

Khi so sánh chuỗi, máy tính sẽ tuân theo sự sắp xếp của các ký tự trong mã hóa. Ví dụ, chữ cái in hoa “A” đứng trước chữ cái thường “a”, vì vậy nó sẽ nhỏ hơn. Do đó:

"An apple" < "a pal" //true

Ví dụ 23. So sánh chuỗi. Chữ in hoa nhỏ hơn chữ thường

Nếu có nhiều ký tự giống nhau liên tiếp, ký tự không bằng đầu tiên được chọn để so sánh.

"An apple" > "A pal" //true

Ví dụ 24. Các chữ cái đầu tiên giống nhau

Biểu thức trong ví dụ 24 là đúng vì khoảng trắng trong mã hóa đứng trước các ký tự chữ cái, và các chữ cái đầu tiên giống nhau.

Nếu một chuỗi đã kết thúc, và chuỗi thứ hai tiếp tục, trong khi phần đầu giống nhau, thì chuỗi đã kết thúc được coi là nhỏ hơn. Ví dụ:

"An apple" < "An apple was found" //true

Ví dụ 25. Độ dài chuỗi khác nhau

Arithmetic operations

Kết quả được xác định bởi kiểu dữ liệu được sử dụng trong biểu thức.

Bạn có thể thực hiện arithmetic operation trên số:

  • Dấu trước số (**-**3) (đôi khi được gọi là “dấu trừ đơn phân”);
  • Nhân (*), chia (/) (đối với số nguyên làm tròn xuống), lấy dư của phép chia (%) (chỉ dành cho số nguyên, 5%2 == 1);
  • Cộng (+), trừ (-);
  • Tăng (++), giảm (--)

Danh sách được đưa ra theo thứ tự ưu tiên.

Tuy nhiên, tốt hơn là không trộn tăng và giảm trong các biểu thức thông thường với các toán tử số học khác, vì có thể có những tình huống mà kết quả không được xác định.

Thao tác (+) cũng được định nghĩa cho chuỗi, nhưng ở đây nó có nghĩa là nối (tạo một chuỗi dài từ hai chuỗi ngắn).

Bitwise operations

Kết quả là một số nguyên.

Đối với số nguyên, cũng có các bitwise operations sau:

  • Phủ định bitwise (~)
  • Dịch phải (>>)
  • Dịch trái (<<)
  • Bitwise “and” (&)
  • Bitwise “or” (|)
  • Exclusive “or” (^)

Nếu bạn cần chúng, thì bạn chắc chắn không còn là người mới bắt đầu và sẽ có thể tìm thông tin cần thiết trong tài liệu ngôn ngữ. :-)

Danh sách được đưa ra theo thứ tự ưu tiên.

Logical operators

Kết quả là logic.

  • Phủ định logic (!)
  • Nhân logic - logic “and” (&&);
  • Cộng logic - logic “or” (||).

Danh sách được đưa ra theo thứ tự ưu tiên.

Có các thao tác khác, sẽ được thảo luận trong các bài viết khác. Ngoài ra, trong các bài viết khác, chúng ta sẽ thảo luận chi tiết hơn về ví dụ sử dụng tất cả các toán tử trên. Hiện tại, bạn có thể sử dụng những gì bạn hiểu hoặc kiểm tra tài liệu ngôn ngữ. Không nên có bất kỳ khó khăn đặc biệt nào.

Ép kiểu

Đôi khi một biểu thức số học liên quan đến nhiều kiểu dữ liệu. Ví dụ, sử dụng hàm Print, chúng ta liên tục in chuỗi cùng với số, và trong bài viết này, chúng ta thậm chí còn gặp phải màu sắc.

Kết quả sẽ là kiểu gì? Ở đây chúng ta có thể chọn từ hai tùy chọn: hoặc chúng ta tự xác định kết quả cuối cùng là gì, hoặc chúng ta tin tưởng vào trình biên dịch.

Trình biên dịch, tất nhiên, thông minh và có thể tìm ra. Nhưng không phải lúc nào cũng vậy.

Vì vậy, hãy xem trình biên dịch làm gì và có thể làm gì “thủ công” để không mất dữ liệu quan trọng, không nhận cảnh báo trong quá trình biên dịch, và tự tin vào kết quả.

Trình biên dịch làm gì?

Đầu tiên, nếu biểu thức sử dụng dữ liệu cùng kiểu, thì kết quả sẽ cùng kiểu. Đây là trường hợp dễ nhất.

Nếu các kiểu khác nhau được liên quan, trình biên dịch sẽ cố gắng mở rộng kết quả đến kiểu chính xác nhất. Ví dụ, nếu chúng ta cố gắng cộng một số nguyên 4 byte (int) với một ngày (datetime), chúng ta sẽ nhận được một ngày (vì phạm vi của nó rộng hơn).

Một literal số nguyên là kiểu int, một literal điểm nổi thường là kiểu double trừ khi kết thúc bằng chữ “f” thường:

5 + 3.4f + 4.25 // Kết quả là double, vì 5 được chuyển đổi thành float trước, sau đó 4.25 đặt độ chính xác double

Ví dụ 26. Ép kiểu khi sử dụng literal

Tài liệu trợ giúp bao gồm sơ đồ ưu tiên ép kiểu:

Ưu tiên ép kiểu

Hình 10. Ưu tiên ép kiểu

Cần lưu ý rằng việc chuyển đổi giữa các kiểu có dấu và không dấu với nhau có thể dẫn đến mất dữ liệu, và chuyển đổi sang kiểu float có thể dẫn đến mất độ chính xác.

Vì vậy, nếu bạn không chắc chắn trình biên dịch sẽ chuyển đổi dữ liệu như thế nào, bạn có thể nghĩ đến việc chỉ định thủ công cái gì nên được chuyển đổi thành cái gì.

Nếu bạn cần chuyển đổi một kết quả (hoặc một giá trị cụ thể) sang một kiểu cụ thể, bạn có thể:

  • Ghi kết quả vào một biến của một kiểu nhất định. Phương pháp này thực chất là một biến thể của ép kiểu tự động, vì vậy phải sử dụng cẩn thận.
  • Sử dụng các hàm chuyển đổi dữ liệu đặc biệt;
  • Sử dụng dạng ngắn của ép kiểu.
(int)a+b // chuyển đổi a thành số nguyên. b giữ nguyên
double (c+d) // hoàn toàn tương tự như trên. Trong trường hợp này, kết quả của phép cộng được chuyển đổi

// và cứ thế - bạn có thể sử dụng bất kỳ kiểu phù hợp nào

Ví dụ 27. Dạng ngắn của ép kiểu

Đừng quên rằng dấu ngoặc cho phép bạn thay đổi thứ tự các thao tác, vì chúng có độ ưu tiên cao nhất. Nếu bạn không chắc chắn, hãy sử dụng dấu ngoặc.

Kết luận

Chúng ta đã đề cập đến một phần lý thuyết lớn về các kiểu dữ liệu cơ bản, biến và biểu thức. Nếu bạn hiểu tài liệu của bài viết này và bài tiếp theo, thì bạn sẽ gần như không còn là người mới bắt đầu, và sẽ chuyển sang một cấp độ cao hơn. Nếu bạn hiểu cách biến (tài liệu của bài viết này) và hàm (sẽ có trong bài tiếp theo) hoạt động, thì bạn có thể tự tin tiến tới các chủ đề phức tạp hơn, như OOP.

OOP, lập trình hướng đối tượng, được coi là tài liệu phức tạp. Chà, có nhiều khó khăn về tư duy hơn là kỹ thuật

Đối với những người không hiểu một số điểm, tôi khuyên bạn nên đọc lại bài viết (một hoặc nhiều lần) rất chậm rãi, từng khái niệm một, kiểm tra mọi thứ được nói trong mã.

Đối với những người hiểu mọi thứ và không muốn đợi cho đến khi tôi sẵn sàng với bài viết tiếp theo, tôi khuyên bạn nên làm sáng lên thời gian chờ bằng cách viết script của riêng bạn sẽ hiển thị thông tin hữu ích về các ký hiệu làm việc và số dư của bạn. Phần lớn thông tin này có thể được lấy từ các họ hàm AccountInfoSymbolInfo.

Hãy thử tìm tên đầy đủ cho mỗi hàm trong các họ này bằng MetaEditor, sau đó tra cứu mô tả của chúng trong trợ giúp. Với những mô tả này và tài liệu được đề cập trong bài viết này, việc tạo script này sẽ không đòi hỏi quá nhiều nỗ lực từ phía bạn.

P.S. Một ví dụ về script như vậy có sẵn trong thư viện chuẩn. Nếu bạn không muốn viết script của riêng mình, hãy thử phân tích cái đã có sẵn. Nếu bạn quản lý để viết một cái của riêng mình, hãy so sánh nó với cái từ thư viện chuẩn.