Initial Commit.

This commit is contained in:
ZhijuCen
2026-06-23 21:47:51 +08:00
commit d17f68e979
5201 changed files with 318318 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
.venv/
__pycache__/
*.log
.extract.log
html_cache/
+1
View File
@@ -0,0 +1 @@
3.14
+87
View File
@@ -0,0 +1,87 @@
# AGENTS.md — mql5-skills Project
## Overview
MQL5 Agent Skills project. Creates and publishes Agent Skills conforming to the
[AgentSkills.io Specification](https://agentskills.io/specification).
- **Project version**: 0.1.0 (pinned during initial phase, not incremented per change)
- **SKILL.md version**: 0.1 (per-document, pinned during initial phase)
- **Language**: Python 3.14, uv-managed
- **Dependencies**: beautifulsoup4, requests
## Directory Structure
```
mql5-skills/
├── AGENTS.md # This file — project conventions
├── README.md # Public readme
├── pyproject.toml # uv project config
├── sitemaps/ # Source sitemaps from mql5.com
│ ├── sitemap_book_en.xml # 581 URLs → programming book
│ └── sitemap_docs_en.xml # 4135 URLs → API reference docs
├── html_cache/ # Downloaded HTML (lossless, gitignored)
│ ├── book/ # 581 .html files
│ └── docs/ # 4135 .html files
├── scripts/ # Extraction scripts (Python)
│ └── extract.py # Main extraction: XML → HTML → Markdown
├── skills/
│ └── mql5/ # The MQL5 development skill
│ ├── SKILL.md # Skill definition (agentskills.io spec)
│ └── references/
│ ├── book/ # Programming book markdown (from sitemap_book_en.xml)
│ │ ├── 0000-book.md
│ │ ├── 00-intro/
│ │ │ ├── 0001-intro.md
│ │ │ └── pics/
│ │ └── ...
│ └── docs/ # API reference markdown (from sitemap_docs_en.xml)
│ ├── 0000-docs.md
│ ├── 01-basis/
│ │ ├── 0001-basis.md
│ │ └── pics/
│ └── ...
└── docs-dev/ # Development documentation
├── extraction.md # Extraction workflow and script design
├── naming.md # Folder/file naming conventions
└── skill-design.md # SKILL.md content plan
```
## SKILL.md Convention (skills/mql5/)
Per agentskills.io spec:
- Frontmatter: `name` (required, max 64 chars, lowercase+hyphens), `description` (required, max 1024 chars)
- Body: Markdown instructions for the agent
- Optional dirs: `scripts/`, `references/`, `assets/`
- Focus areas: positions, orders, indicators, ticks, bars
## Extraction Workflow
Two-phase pipeline (network only needed for Phase 1):
```
Phase 1: download — sitemap → fetch HTML → html_cache/
Phase 2: convert — html_cache/ → parse HTML → download images → .md files
```
- Script: `scripts/extract.py` with `download`, `convert`, `debug` subcommands
- HTML cache: `html_cache/book/`, `html_cache/docs/` (lossless, gitignored)
- Output: `skills/mql5/references/book/`, `skills/mql5/references/docs/`
- Each sitemap URL → one .md file + corresponding .html in cache
- Images → `pics/` subfolder within chapter dir
- Debug target: TesterStatistics page (mixed content: tables, images, code, console output)
## Naming Convention
See `docs-dev/naming.md` for full specification. Key rules:
- 4-digit sequential prefix per file within a chapter folder
- 2-digit prefix on chapter folder names
- Chapter folder names derived from URL path segments
- Max one level of subfolder under `book/` or `docs/`
- Each chapter folder contains a `pics/` subfolder
## Git Workflow
- Conventional commits
- Do not commit .venv, __pycache__
- Tag releases per semver
View File
+150
View File
@@ -0,0 +1,150 @@
# Extraction Workflow — Web Content to Markdown
Two-phase pipeline: download HTML (lossless) → convert to Markdown (offline).
```
Phase 1: sitemap XML → URL list → fetch HTML → save to html_cache/
Phase 2: html_cache/ HTML → parse → download images → write Markdown
```
- **Input**: `sitemaps/sitemap_book_en.xml`, `sitemaps/sitemap_docs_en.xml`
- **HTML cache**: `html_cache/book/`, `html_cache/docs/`
- **Output**: `skills/mql5/references/book/`, `skills/mql5/references/docs/`
- **Tool**: Python script using `requests` + `beautifulsoup4`
## Script — `scripts/extract.py`
### CLI
```bash
# Phase 1: download HTML
python scripts/extract.py download --sitemap sitemaps/sitemap_book_en.xml
python scripts/extract.py download --all
# Phase 2: convert to Markdown
python scripts/extract.py convert --sitemap sitemaps/sitemap_book_en.xml
python scripts/extract.py convert --all
# Debug single page (fetches + analyzes)
python scripts/extract.py debug URL
```
Options (download/convert):
- `--sitemap PATH` — single sitemap
- `--all` — all known sitemaps
- `--force` — re-process everything (ignore progress log)
- `--dry-run` — show plan without processing
### Phase 1 — Download
1. Parse sitemap XML → URL list
2. Build file map (naming convention, see `naming.md`)
3. For each URL:
- Skip if already cached (idempotent)
- Fetch HTML with retry + rate limiting (0.5s delay)
- Save raw HTML to `html_cache/{label}/{NN-chapter}/{NNNN-name}.html`
4. Progress: `html_cache/{label}/.download.log`
HTML files are saved as-is — no parsing, no transformation.
### Phase 2 — Convert
1. Parse sitemap XML → URL list (same mapping)
2. For each URL:
- Skip if already converted (idempotent)
- Read local HTML from `html_cache/`
- Parse `#help` content → elements
- Convert elements to Markdown
- Download images to `pics/` subfolder
- Write `.md` file
3. Progress: `{output_dir}/.convert.log`
### HTML Cache Structure
```
html_cache/
├── book/
│ ├── 0000-book.html
│ ├── .download.log
│ ├── 00-intro/
│ │ ├── 0001-intro.html
│ │ ├── 0002-intro-edit-compile-run.html
│ │ └── ...
│ └── ...
└── docs/
├── 0000-docs.html
├── .download.log
├── 00-basis/
│ ├── 0001-basis.html
│ └── ...
└── ...
```
Filenames mirror the markdown output (same 4-digit prefix, same derived name).
## Content Extraction Rules
### Main Content Selector
```css
#help > h1:nth-child(1)
```
Extract from this `<h1>` through to the last content element.
**Fallback**: If no `h1` exists (e.g. root pages like `/en/book`), use the
first `<Tag>` child of `#help` as the start element.
### Table Handling
| Has `<thead>` | Meaning | Render as |
|---------------|---------|-----------|
| Yes | Data table with headers | Markdown table with `|---|` separator |
| No | Code block or console output | Fenced code block (` ``` `) |
Tables without `<thead>` on mql5.com typically contain:
- MQL5 code (with `<span>` color classes for syntax highlighting)
- MetaTrader 5 Strategy Tester console output
### Image Handling
The `<img>` is often nested inside `<p class="p_ImageCaption">`.
**Alt text extraction**:
1. Try `img.find_next_sibling("p")`
2. If not found, try `img.parent.find_next_sibling("p")`
3. Fall back to `img.get("alt", "")`
**`<p>` containing `<img>`**: The `<p>` is treated as an image block —
the `<img>` is routed through the image download path.
### Code Blocks
- `<pre><code>` → fenced code block with language detection
- Language detected from CSS classes (`mql`, `cpp`, `sql`, etc.)
- Text whitespace preserved (no space-joining)
## Debug Target — TesterStatistics
URL: `https://www.mql5.com/en/book/automation/tester/tester_testerstatistics`
Content boundaries:
- Start: `#help > h1:nth-child(1)` → "Getting testing financial statistics: TesterStatistics"
- End: `p.p_Text:nth-child(45)`
Exercises: data tables (thead), code blocks (no thead), images, console output.
## Error Handling
| Error | Phase 1 | Phase 2 |
|-------|---------|---------|
| HTTP 404 | Skip, log, continue | N/A (offline) |
| HTTP 429/5xx | Retry with backoff | N/A |
| Missing HTML | N/A | Log warning, skip |
| Parse error | N/A | Log error, skip |
| Image download fail | N/A | Log warning, use original URL |
## Resumability
Each phase has its own progress log. Re-running skips completed items.
Use `--force` to re-process everything.
+147
View File
@@ -0,0 +1,147 @@
# Naming Convention — Folder & File Structure
This document specifies how sitemap URLs map to the file tree under
`skills/mql5/references/book/` and `skills/mql5/references/docs/`.
## Core Rules
1. **One subfolder level**: Under `book/` or `docs/`, at most one level of
subfolder (the "chapter folder") is used. All pages within that chapter are
`.md` files directly inside the chapter folder.
2. **`pics/` exception**: Each chapter folder may contain a `pics/` subfolder
for images extracted from that chapter's pages.
3. **Sequential numbering**: Both chapter folders and files carry numeric
prefixes that reflect their order in the source sitemap.
## Chapter Folders
A "chapter" corresponds to the **first URL path segment** after the sitemap root.
| Source URL pattern | Chapter folder |
|------------------------------|--------------------|
| `/en/book/intro` ... | `00-intro/` |
| `/en/book/basis` ... | `01-basis/` |
| `/en/docs/basis` ... | `00-basis/` |
| `/en/docs/standardlibrary` … | `34-standardlibrary/` |
- **Prefix**: 2-digit zero-padded number (00, 01, 02, …)
- **Suffix**: the URL path segment (e.g. `intro`, `basis`, `standardlibrary`)
- **Order**: by first occurrence in the sitemap
Chapters are numbered **globally** within each sitemap (book and docs are
independent numbering spaces).
## Files Within a Chapter
Each URL that belongs to a chapter becomes one `.md` file inside the chapter
folder. The filename encodes:
```
{4-digit global number}-{derived-name}.md
```
- **4-digit prefix**: sequential number matching the URL's position in the
sitemap (0001, 0002, …). This is **global** across the entire sitemap, not
reset per chapter.
- **Derived name**: the URL path **after** the depth-1 segment, with `/`
replaced by `-`. If the URL **is** the depth-1 segment itself (the chapter
index page), the name is the segment name.
### Derived-name rules
| URL path (after `/en/{book|docs}/`) | Chapter folder | File name |
|-------------------------------------|----------------|---------------------|
| `intro` | `00-intro/` | `0001-intro.md` |
| `intro/edit_compile_run` | `00-intro/` | `0002-edit-compile-run.md` |
| `basis` | `01-basis/` | `0015-basis.md` |
| `basis/syntax` | `01-basis/` | `0016-basis-syntax.md` |
| `basis/types/integer/integertypes` | `01-basis/` | `0019-basis-types-integer-integertypes.md` |
| `standardlibrary/mathematics/...` | `34-standardlibrary/` | `1143-standardlibrary-mathematics-....md` |
### Root-level URLs
The very first URL in each sitemap (e.g. `https://www.mql5.com/en/book` or
`https://www.mql5.com/en/docs`) has no depth-1 segment. It is placed directly
under the references root as:
```
0000-book.md (for book sitemap)
0000-docs.md (for docs sitemap)
```
## Complete Example — Book (first 20)
```
skills/mql5/references/book/
├── 0000-book.md # /en/book
├── 00-intro/
│ ├── 0001-intro.md # /en/book/intro
│ ├── 0002-edit-compile-run.md # /en/book/intro/edit_compile_run
│ ├── 0003-mql-wizard.md # /en/book/intro/mql_wizard
│ ├── ...
│ └── pics/
├── 01-basis/
│ ├── 0015-basis.md # /en/book/basis
│ ├── 0016-basis-identifiers.md # /en/book/basis/identifiers
│ ├── 0017-basis-builtin-types.md # /en/book/basis/builtin_types
│ ├── 0018-basis-builtin-types-integer-numbers.md
│ ├── ...
│ └── pics/
├── 02-oop/
│ ├── ...
│ └── pics/
...
```
## Complete Example — Docs (first 20)
```
skills/mql5/references/docs/
├── 0000-docs.md # /en/docs
├── 00-basis/
│ ├── 0001-basis.md # /en/docs/basis
│ ├── 0002-basis-syntax.md # /en/docs/basis/syntax
│ ├── 0003-basis-syntax-commentaries.md # /en/docs/basis/syntax/commentaries
│ ├── 0004-basis-syntax-identifiers.md
│ ├── 0005-basis-syntax-reserved.md
│ ├── 0006-basis-types.md
│ ├── 0007-basis-types-integer.md
│ ├── 0008-basis-types-integer-integertypes.md
│ ├── ...
│ └── pics/
├── 01-constants/
│ ├── ...
│ └── pics/
...
```
## Image Handling
- Images are saved to `pics/` within the chapter folder where they appear.
- Filenames: `{original-filename}` or `{derived-name}.png` if no useful name.
- The Markdown reference uses a relative path: `![alt](pics/image.png)`.
- Per extraction spec: the `<img>` alt text is taken from the next sibling
`<p>` element's innerHTML, not duplicated.
## Name Sanitization
When converting URL segments to file/folder names:
| Character | Replacement |
|-----------|-------------|
| `_` | `-` |
| `/` | `-` (within filename) |
| Uppercase | lowercase |
| Non-alphanumeric (except `-`) | removed |
Example: `builtin_types``builtin-types`, `MQL_Wizard``mql-wizard`.
## Validation
A post-extraction script should verify:
1. Every sitemap URL has exactly one output file.
2. File numbers are sequential with no gaps.
3. No file exceeds the 4-digit range (max 9999 URLs per sitemap).
4. Chapter folder names match the expected pattern: `{NN}-{segment}`.
+114
View File
@@ -0,0 +1,114 @@
# SKILL.md Design — skills/mql5
This document describes the content and structure of `skills/mql5/SKILL.md`,
conforming to the [AgentSkills.io Specification](https://agentskills.io/specification).
## Frontmatter
```yaml
---
name: mql5
description: >
MQL5 development skill for MetaTrader 5. Covers Expert Advisors, Indicators,
Scripts, and Services. Focus on positions, orders, indicators, ticks, and bars.
Includes programming book and API reference documentation.
version: "0.1"
license: MIT
compatibility: >
Target: MetaTrader 5 platform. Language: MQL5 (C++-like syntax).
File extensions: *.mq5 (source), *.mqh (headers).
References: mql5.com/en/book (programming), mql5.com/en/docs (API).
metadata:
project-version: "0.1.0"
sources:
book: sitemaps/sitemap_book_en.xml (581 URLs)
docs: sitemaps/sitemap_docs_en.xml (4135 URLs)
focus-areas:
- positions
- orders
- indicators
- ticks
- bars
---
```
## Body Content Structure
The body should be structured as follows:
### 1. Overview
Brief description of MQL5 and MetaTrader 5:
- MQL5 is the programming language for MetaTrader 5
- Syntax similar to C++
- File types: `.mq5` (source), `.mqh` (headers)
- Program types: Expert Advisors, Indicators, Scripts, Services
### 2. Quick Reference — Key Operations
Focus areas with concise API patterns:
#### Positions
- `CTrade` class for position management
- `PositionGetSymbol()`, `PositionSelect()`, `PositionGetDouble()`
- `CTrade::PositionOpen()`, `CTrade::PositionClose()`
#### Orders
- `CTrade::OrderSend()` for pending orders
- `ORDER_TYPE_BUY_LIMIT`, `ORDER_TYPE_SELL_LIMIT`, etc.
- `OrderGetTicket()`, `OrderSelect()`
#### Indicators
- `iMA()`, `iRSI()`, `iMACD()`, `iBands()` — built-in indicators
- `CopyBuffer()` to read indicator values
- `IndicatorCreate()` for custom indicators
#### Ticks
- `SymbolInfoTick()` — current tick data
- `MqlTick` structure: `bid`, `ask`, `last`, `volume`, `time`
- `OnTick()` handler for Expert Advisors
#### Bars
- `Bars()`, `BarsCalculated()` — bar count
- `CopyOpen()`, `CopyHigh()`, `CopyLow()`, `CopyClose()`, `CopyVolume()`
- `CopyRates()`, `CopyTime()`
- `CTerminalInfo`, `CSymbolInfo` for symbol/bar info
### 3. Program Types
| Type | Purpose | Key Handler |
|------|---------|-------------|
| Expert Advisor | Automated trading | `OnTick()`, `OnInit()`, `OnDeinit()` |
| Indicator | Technical analysis | `OnCalculate()` |
| Script | One-shot execution | `OnStart()` |
| Service | Background task | `OnStart()`, `OnTimer()` |
### 4. Common Patterns
- Trade execution with error handling
- Indicator buffer management
- Timer-based operations
- Chart object manipulation
- File I/O for logging/data
### 5. References
Point to the extracted documentation:
- `references/book/` — Programming book (learning path)
- `references/docs/` — API reference (function/type lookup)
### 6. Pitfalls & Best Practices
- `RefreshRates()` before trading operations
- `NormalizeDouble()` for price comparisons
- Check `Retcode()` after trade operations
- Use `CTrade` class over raw `OrderSend()`
- Handle `OnTimer()` for periodic operations
- Test with Strategy Tester before live deployment
## Implementation Notes
- The SKILL.md should be concise (< 1024 chars for description, body can be longer)
- Body is loaded as context by AI agents — prioritize actionable patterns
- Reference files provide depth; SKILL.md provides the "what to do"
- Version 0.1: initial content, will expand as extraction completes
+27
View File
@@ -0,0 +1,27 @@
# MT5 XAUUSD Order Block and Liquidity Expert Advisor
[Origin](https://www.mql5.com/en/job/250013)
## Specification
* Use the H4 timeframe to determine the main trend direction.
* Identify valid order blocks on the H1 timeframe.
* Identify liquidity zones and liquidity sweeps.
* Wait for M15 confirmation before opening a trade.
* Only take buy trades in bullish market conditions and sell trades in bearish market conditions.
* Risk a fixed percentage of account balance per trade (user adjustable).
* Automatically calculate lot size based on risk.
* Place Stop Loss below/above the order block.
* Place Take Profit at a minimum 1:2 risk-to-reward ratio.
* Allow only one trade per signal.
* Include settings that can be adjusted by the user.
* Send MT5 mobile notifications when trades are opened and closed.
* Work on MT5 and Exness accounts.
## Deliverables
* Compiled EA (.ex5)
* Source code (.mq5)
* Installation instructions
* Backtest results
+10
View File
@@ -0,0 +1,10 @@
[project]
name = "mql5-skills"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.14"
dependencies = [
"beautifulsoup4>=4.15.0",
"requests>=2.34.2",
]
+37
View File
@@ -0,0 +1,37 @@
browser,profile,user_agent
"Safari 26.3","Desktop · macOS","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.3 Safari/605.1.15"
"Firefox 148","Desktop · Linux","Mozilla/5.0 (X11; Linux x86_64; rv:148.0) Gecko/20100101 Firefox/148.0"
"Chrome 145","Desktop · Windows","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36"
"Edge 144","Desktop · Linux","Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.0.0"
"Edge 143","Desktop · Windows","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0"
"Firefox 146","Desktop · Windows","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:146.0) Gecko/20100101 Firefox/146.0"
"Chrome 143","Desktop · Windows","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36"
"Chrome 143","Desktop · Linux","Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36"
"Firefox 147","Desktop · Windows","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0"
"Edge 145","Desktop · Linux","Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36 Edg/145.0.0.0"
"Safari 26.2","Desktop · macOS","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Safari/605.1.15"
"Firefox 147","Desktop · macOS","Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:147.0) Gecko/20100101 Firefox/147.0"
"Edge 143","Desktop · macOS","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0"
"Chrome 143","Desktop · macOS","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36"
"Firefox 146","Desktop · macOS","Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:146.0) Gecko/20100101 Firefox/146.0"
"Edge 145","Desktop · Windows","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36 Edg/145.0.0.0"
"Chrome 144","Desktop · macOS","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36"
"Firefox 148","Desktop · Windows","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:148.0) Gecko/20100101 Firefox/148.0"
"Edge 144","Desktop · Windows","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.0.0"
"Chrome 144","Desktop · Linux","Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36"
"Safari 26.0","Desktop · macOS","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Safari/605.1.15"
"Firefox 146","Desktop · Linux","Mozilla/5.0 (X11; Linux x86_64; rv:146.0) Gecko/20100101 Firefox/146.0"
"Edge 143","Desktop · Linux","Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0"
"Chrome 145","Desktop · Linux","Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36"
"Edge 144","Desktop · macOS","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.0.0"
"Chrome 145","Desktop · macOS","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36"
"Firefox 147","Desktop · Linux","Mozilla/5.0 (X11; Linux x86_64; rv:147.0) Gecko/20100101 Firefox/147.0"
"Chrome 144","Desktop · Windows","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36"
"Edge 145","Desktop · macOS","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36 Edg/145.0.0.0"
"Firefox 148","Desktop · macOS","Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:148.0) Gecko/20100101 Firefox/148.0"
"Safari 26.2","Desktop · macOS","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Safari/605.1.15"
"Chrome 143","Desktop · Windows","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36"
"Edge 144","Desktop · Windows","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.0.0"
"Firefox 148","Desktop · macOS","Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:148.0) Gecko/20100101 Firefox/148.0"
"Chrome 144","Desktop · Linux","Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36"
"Firefox 146","Desktop · Windows","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:146.0) Gecko/20100101 Firefox/146.0"
1 browser profile user_agent
2 Safari 26.3 Desktop · macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.3 Safari/605.1.15
3 Firefox 148 Desktop · Linux Mozilla/5.0 (X11; Linux x86_64; rv:148.0) Gecko/20100101 Firefox/148.0
4 Chrome 145 Desktop · Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36
5 Edge 144 Desktop · Linux Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.0.0
6 Edge 143 Desktop · Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0
7 Firefox 146 Desktop · Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:146.0) Gecko/20100101 Firefox/146.0
8 Chrome 143 Desktop · Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36
9 Chrome 143 Desktop · Linux Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36
10 Firefox 147 Desktop · Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0
11 Edge 145 Desktop · Linux Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36 Edg/145.0.0.0
12 Safari 26.2 Desktop · macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Safari/605.1.15
13 Firefox 147 Desktop · macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:147.0) Gecko/20100101 Firefox/147.0
14 Edge 143 Desktop · macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0
15 Chrome 143 Desktop · macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36
16 Firefox 146 Desktop · macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:146.0) Gecko/20100101 Firefox/146.0
17 Edge 145 Desktop · Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36 Edg/145.0.0.0
18 Chrome 144 Desktop · macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36
19 Firefox 148 Desktop · Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:148.0) Gecko/20100101 Firefox/148.0
20 Edge 144 Desktop · Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.0.0
21 Chrome 144 Desktop · Linux Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36
22 Safari 26.0 Desktop · macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Safari/605.1.15
23 Firefox 146 Desktop · Linux Mozilla/5.0 (X11; Linux x86_64; rv:146.0) Gecko/20100101 Firefox/146.0
24 Edge 143 Desktop · Linux Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0
25 Chrome 145 Desktop · Linux Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36
26 Edge 144 Desktop · macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.0.0
27 Chrome 145 Desktop · macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36
28 Firefox 147 Desktop · Linux Mozilla/5.0 (X11; Linux x86_64; rv:147.0) Gecko/20100101 Firefox/147.0
29 Chrome 144 Desktop · Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36
30 Edge 145 Desktop · macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36 Edg/145.0.0.0
31 Firefox 148 Desktop · macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:148.0) Gecko/20100101 Firefox/148.0
32 Safari 26.2 Desktop · macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Safari/605.1.15
33 Chrome 143 Desktop · Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36
34 Edge 144 Desktop · Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.0.0
35 Firefox 148 Desktop · macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:148.0) Gecko/20100101 Firefox/148.0
36 Chrome 144 Desktop · Linux Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36
37 Firefox 146 Desktop · Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:146.0) Gecko/20100101 Firefox/146.0
+974
View File
@@ -0,0 +1,974 @@
#!/usr/bin/env python3
"""
Two-phase MQL5 content extraction: download HTML → convert to Markdown.
Phase 1 (download): Fetch all HTML from mql5.com, save losslessly to html_cache/
Phase 2 (convert): Parse local HTML, download images, write Markdown files
Usage:
# Phase 1: download all HTML
python scripts/extract.py download --sitemap sitemaps/sitemap_book_en.xml
python scripts/extract.py download --sitemap sitemaps/sitemap_docs_en.xml
python scripts/extract.py download --all
# Phase 2: convert HTML → Markdown
python scripts/extract.py convert --sitemap sitemaps/sitemap_book_en.xml
python scripts/extract.py convert --sitemap sitemaps/sitemap_docs_en.xml
python scripts/extract.py convert --all
# Debug single page (fetches + converts)
python scripts/extract.py debug URL
"""
import argparse
import hashlib
import logging
import os
import random
import re
import time
import xml.etree.ElementTree as ET
from pathlib import Path
from urllib.parse import urlparse
import requests
from bs4 import BeautifulSoup, NavigableString, PageElement, Tag
# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------
USER_AGENTS = {
"firefox": "Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0",
"msedge": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36 Edg/148.0.0.0",
"chromium": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36",
}
UA_KEYS = list(USER_AGENTS.keys())
_NUM_UA = len(UA_KEYS)
def load_user_agents_csv(csv_path: str) -> None:
"""Load User-Agent strings from a CSV file, replacing module-level UA pool.
CSV must have a ``user_agent`` column. Each row becomes one entry;
keys are ``u0``, ``u1``, … in file order.
"""
global USER_AGENTS, UA_KEYS, _NUM_UA
import csv as _csv
with open(csv_path, newline="", encoding="utf-8") as f:
reader = _csv.DictReader(f)
if "user_agent" not in (reader.fieldnames or []):
raise ValueError(f"CSV {csv_path} has no 'user_agent' column")
pool = {f"u{i}": row["user_agent"] for i, row in enumerate(reader)}
if not pool:
raise ValueError(f"CSV {csv_path} contains no User-Agent rows")
USER_AGENTS = pool
UA_KEYS = list(USER_AGENTS.keys())
_NUM_UA = len(UA_KEYS)
log.info("Loaded %d User-Agents from %s", _NUM_UA, csv_path)
def get_user_agent(index: int | None = None) -> str:
"""Return a User-Agent string. Deterministic by index, or random."""
if index is not None:
return USER_AGENTS[UA_KEYS[index % _NUM_UA]]
return USER_AGENTS[random.choice(UA_KEYS)]
REQUEST_DELAY = 0.9 # seconds between requests
MAX_RETRIES = 3
RETRY_BACKOFF = 2 # exponential backoff multiplier
SITEMAP_NS = {"s": "http://www.sitemaps.org/schemas/sitemap/0.9"}
# CSS selector for main content start
CONTENT_START_SELECTOR = "#help > h1:nth-child(1)"
# HTML cache root
HTML_CACHE_ROOT = Path("html_cache")
# Mapping: sitemap path → (output md dir, label)
SITEMAP_DEFAULTS = {
"sitemaps/sitemap_book_en.xml": (
"skills/mql5/references/book",
"book",
),
"sitemaps/sitemap_docs_en.xml": (
"skills/mql5/references/docs",
"docs",
),
}
# ---------------------------------------------------------------------------
# Logging
# ---------------------------------------------------------------------------
log = logging.getLogger("extract")
# ---------------------------------------------------------------------------
# Sitemap parsing
# ---------------------------------------------------------------------------
def parse_sitemap(sitemap_path: str) -> list[str]:
"""Extract <loc> URLs from a sitemap XML file, in document order."""
tree = ET.parse(sitemap_path)
urls = [loc.text for loc in tree.findall(".//s:url/s:loc", SITEMAP_NS)]
log.info("Parsed %d URLs from %s", len(urls), sitemap_path)
return urls
# ---------------------------------------------------------------------------
# URL → file mapping (naming convention, shared by both phases)
# ---------------------------------------------------------------------------
def sanitize_name(name: str) -> str:
"""Convert a URL segment to a safe file/folder name."""
name = name.lower()
name = name.replace("_", "-")
name = re.sub(r"[^a-z0-9-]", "", name)
name = re.sub(r"-{2,}", "-", name)
name = name.strip("-")
return name
def derive_name_from_url(url: str, root_prefix: str) -> str:
"""Given a full URL and the root prefix (e.g. '/en/book/'),
return the 'path tail' used for naming."""
path = url
if path.startswith("http"):
parsed = urlparse(path)
path = parsed.path
path_clean = path.strip("/")
prefix_clean = root_prefix.strip("/")
if path_clean.lower() == prefix_clean.lower():
return ""
if path_clean.lower().startswith(prefix_clean.lower() + "/"):
return path_clean[len(prefix_clean) + 1 :]
return path_clean
def build_file_map(
urls: list[str], root_prefix: str, output_dir: str, label: str
) -> list[dict]:
"""Build the mapping from URL index → file path.
Returns a list of dicts with keys:
url, index (0-based), file_path (.md), html_path (.html),
chapter_folder, filename
"""
html_cache_dir = HTML_CACHE_ROOT / label
file_map = []
chapter_counter = 0
chapter_map: dict[str, int] = {}
for idx, url in enumerate(urls):
tail = derive_name_from_url(url, root_prefix)
parts = tail.split("/") if tail else []
if not parts:
# Root URL
base = f"{idx:04d}-{label}"
filename_md = f"{base}.md"
filename_html = f"{base}.html"
file_path = Path(output_dir) / filename_md
html_path = html_cache_dir / filename_html
chapter_folder = None
else:
depth1 = parts[0]
if depth1 not in chapter_map:
chapter_map[depth1] = chapter_counter
chapter_counter += 1
ch_num = chapter_map[depth1]
chapter_folder = f"{ch_num:02d}-{sanitize_name(depth1)}"
if len(parts) == 1:
derived = sanitize_name(depth1)
else:
derived = "-".join(sanitize_name(p) for p in parts)
base = f"{idx:04d}-{derived}"
filename_md = f"{base}.md"
filename_html = f"{base}.html"
file_path = Path(output_dir) / chapter_folder / filename_md
html_path = html_cache_dir / chapter_folder / filename_html
file_map.append(
{
"url": url,
"index": idx,
"file_path": str(file_path),
"html_path": str(html_path),
"chapter_folder": chapter_folder,
"filename": filename_md,
}
)
log.info(
"Built file map: %d files, %d chapters",
len(file_map),
chapter_counter,
)
return file_map
# ---------------------------------------------------------------------------
# Phase 1: Download HTML
# ---------------------------------------------------------------------------
def fetch_html(url: str, session: requests.Session) -> str | None:
"""Fetch a URL with retry + backoff. Returns HTML text or None on failure."""
for attempt in range(1, MAX_RETRIES + 1):
try:
resp = session.get(url, timeout=30)
if resp.status_code == 200:
return resp.text
if resp.status_code in (429, 500, 502, 503, 504):
wait = RETRY_BACKOFF**attempt
log.warning(
"HTTP %d for %s, retry %d/%d in %ds",
resp.status_code, url, attempt, MAX_RETRIES, wait,
)
time.sleep(wait)
continue
log.error("HTTP %d for %s — skipping", resp.status_code, url)
return None
except requests.RequestException as exc:
wait = RETRY_BACKOFF**attempt
log.warning(
"Request error for %s: %s, retry %d/%d in %ds",
url, exc, attempt, MAX_RETRIES, wait,
)
time.sleep(wait)
log.error("All %d retries failed for %s", MAX_RETRIES, url)
return None
def download_one(url: str, html_path: str, session: requests.Session) -> bool:
"""Download a single URL and save to html_path. Returns True on success."""
out = Path(html_path)
if out.exists():
log.debug("Already cached: %s", html_path)
return True
html = fetch_html(url, session)
if html is None:
return False
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(html, encoding="utf-8")
return True
def run_download(
sitemap_path: str,
force: bool = False,
dry_run: bool = False,
random_ua: bool = False,
random_delay: bool = False,
user_agents_csv: str | None = None,
) -> None:
"""Phase 1: download all HTML files for a sitemap."""
if user_agents_csv:
load_user_agents_csv(user_agents_csv)
urls = parse_sitemap(sitemap_path)
root_prefix = _root_prefix_from_sitemap(sitemap_path)
label = _label_from_sitemap(sitemap_path)
md_output_dir = SITEMAP_DEFAULTS.get(sitemap_path, ("", label))[0]
file_map = build_file_map(urls, root_prefix, md_output_dir, label)
if dry_run:
print(f"\n=== Download dry run: {sitemap_path} ===")
print(f"Total URLs: {len(file_map)}")
print(f"Cache dir: {HTML_CACHE_ROOT / label}")
for entry in file_map[:20]:
print(f" [{entry['index']:4d}] {entry['url']}")
print(f"{entry['html_path']}")
if len(file_map) > 20:
print(f" ... ({len(file_map) - 20} more)")
return
# Progress log
log_path = HTML_CACHE_ROOT / label / ".download.log"
processed = _load_log(log_path) if not force else set()
session = requests.Session()
success = 0
skipped = 0
failed = 0
for entry in file_map:
url = entry["url"]
if url in processed and not force:
skipped += 1
continue
session.headers.update({
"User-Agent": get_user_agent() if random_ua else get_user_agent(entry["index"])
})
ok = download_one(url, entry["html_path"], session)
if ok:
success += 1
_append_log(log_path, url)
log.info("[%d/%d] OK: %s", success + skipped + failed, len(file_map), url)
else:
failed += 1
log.error("[%d/%d] FAIL: %s", success + skipped + failed, len(file_map), url)
if random_delay:
time.sleep(REQUEST_DELAY * random.uniform(1.0, 3.0))
else:
time.sleep(REQUEST_DELAY)
log.info(
"Done: %d success, %d skipped, %d failed (total %d)",
success, skipped, failed, len(file_map),
)
# ---------------------------------------------------------------------------
# Phase 2: Convert HTML → Markdown
# ---------------------------------------------------------------------------
# Image downloading (only needed during convert phase)
def download_image(
img_url: str, pics_dir: Path, session: requests.Session
) -> str | None:
"""Download an image, save to pics_dir, return local filename or None."""
parsed = urlparse(img_url)
name = Path(parsed.path).name
if not name or "." not in name:
ext = ".png"
name = hashlib.md5(img_url.encode()).hexdigest()[:12] + ext
local_path = pics_dir / name
if local_path.exists():
return name
try:
resp = session.get(img_url, timeout=30)
if resp.status_code == 200:
local_path.parent.mkdir(parents=True, exist_ok=True)
local_path.write_bytes(resp.content)
return name
except requests.RequestException as exc:
log.warning("Failed to download image %s: %s", img_url, exc)
return None
# HTML → Markdown conversion helpers
def get_text(el: Tag | NavigableString, sep: str = "") -> str:
"""Recursively get text content."""
if isinstance(el, NavigableString):
return str(el)
parts = []
for child in el.children:
t = get_text(child, sep)
if t:
parts.append(t)
return sep.join(parts)
def inline_to_md(el: Tag) -> str:
"""Convert an inline element to Markdown."""
if isinstance(el, NavigableString):
return str(el)
tag = el.name
inner = "".join(inline_to_md(c) for c in el.children)
if tag in ("strong", "b"):
return f"**{inner}**"
if tag in ("em", "i"):
return f"*{inner}*"
if tag == "code":
if "`" in inner:
return f"`` {inner} ``"
return f"`{inner}`"
if tag == "a":
href = el.get("href", "")
if href and not href.startswith("#"):
return f"[{inner}]({href})"
return inner
if tag == "br":
return " \n"
if tag == "img":
src = el.get("src", "")
alt = el.get("alt", "")
return f"![{alt}]({src})"
if tag in ("span", "font", "u", "s", "sub", "sup", "small", "big", "mark"):
return inner
return inner
def table_to_md(table: Tag) -> str:
"""Convert a <table> to Markdown.
Has <thead> → data table. No <thead> → code block.
"""
thead = table.find("thead")
tbody = table.find("tbody") or table
if thead:
rows = []
for tr in thead.find_all("tr"):
cells = [get_text(td, " ").strip() for td in tr.find_all(["th", "td"])]
rows.append(cells)
ncols = max((len(r) for r in rows), default=0)
if rows and ncols:
rows.append(["---"] * ncols)
body_rows = tbody.find_all("tr") if tbody else []
for tr in body_rows:
if thead and tr.find_parent("thead"):
continue
cells = [get_text(td, " ").strip() for td in tr.find_all(["th", "td"])]
rows.append(cells)
lines = []
for row in rows:
while len(row) < ncols:
row.append("")
lines.append("| " + " | ".join(row) + " |")
return "\n".join(lines)
else:
code_lines = []
all_rows = tbody.find_all("tr") if tbody else table.find_all("tr")
for tr in all_rows:
cells = tr.find_all(["td", "th"])
for cell in cells:
code_el = cell.find("code") or cell.find("pre")
if code_el:
code_lines.append(get_text(code_el, "\n"))
else:
text = cell.get_text()
if text.strip():
code_lines.append(text)
code_text = "\n".join(code_lines)
lang = detect_code_lang(table)
return f"```{lang}\n{code_text}\n```"
def detect_code_lang(el: Tag) -> str:
"""Try to detect code language from CSS classes."""
classes = el.get("class", [])
if isinstance(classes, str):
classes = classes.split()
for cls in classes:
cl = cls.lower()
if "mql" in cl or "cpp" in cl or "c-plus" in cl:
return "cpp"
if "console" in cl or "output" in cl or "result" in cl:
return ""
if "sql" in cl:
return "sql"
parent = el.parent
if parent and isinstance(parent, Tag):
return detect_code_lang(parent)
return ""
def pre_to_md(pre: Tag) -> str:
"""Convert <pre><code> to fenced Markdown code."""
code = pre.find("code")
if code:
lang = detect_code_lang(code)
text = get_text(code, "\n")
else:
lang = ""
text = get_text(pre, "\n")
return f"```{lang}\n{text}\n```"
def element_to_md(
el: Tag,
pics_dir: Path | None,
session: requests.Session | None,
base_url: str,
img_cache: dict[str, str | None],
) -> str:
"""Convert a single content element to Markdown string."""
if isinstance(el, NavigableString):
text = str(el).strip()
return text if text else ""
tag = el.name
# --- Headings ---
if tag in ("h1", "h2", "h3", "h4", "h5", "h6"):
level = int(tag[1])
text = get_text(el, " ").strip()
return f"{'#' * level} {text}\n"
# --- Paragraphs ---
if tag == "p":
img_child = el.find("img")
if img_child:
return element_to_md(img_child, pics_dir, session, base_url, img_cache)
inner = "".join(inline_to_md(c) for c in el.children)
inner = inner.strip()
if not inner:
return ""
return f"{inner}\n"
# --- Images ---
if tag == "img":
src = el.get("src", "")
if not src:
return ""
if src.startswith("/"):
src = f"https://www.mql5.com{src}"
elif not src.startswith("http"):
src = f"{base_url.rstrip('/')}/{src}"
# Get alt text
alt_text = ""
next_sibling = el.find_next_sibling("p")
if not next_sibling and el.parent and isinstance(el.parent, Tag):
next_sibling = el.parent.find_next_sibling("p")
if next_sibling:
alt_text = get_text(next_sibling, " ").strip()
if not alt_text:
alt_text = el.get("alt", "")
# Download image
img_filename = None
if pics_dir and session:
if src in img_cache:
img_filename = img_cache[src]
else:
img_filename = download_image(src, pics_dir, session)
img_cache[src] = img_filename
if img_filename:
return f"![{alt_text}](pics/{img_filename})\n"
else:
return f"![{alt_text}]({src})\n"
# --- Tables ---
if tag == "table":
return table_to_md(el) + "\n"
# --- Preformatted code ---
if tag == "pre":
return pre_to_md(el) + "\n"
# --- Lists ---
if tag in ("ul", "ol"):
items = []
for i, li in enumerate(el.find_all("li", recursive=False)):
prefix = f"{i+1}. " if tag == "ol" else "- "
# If <li> contains an <img>, handle it through element_to_md
img_child = li.find("img")
if img_child:
img_md = element_to_md(img_child, pics_dir, session, base_url, img_cache)
# Get remaining text after the image
rest = "".join(inline_to_md(c) for c in li.children if not (isinstance(c, Tag) and c.name == "img"))
inner = (img_md.strip() + " " + rest.strip()).strip()
else:
inner = "".join(inline_to_md(c) for c in li.children)
items.append(f"{prefix}{inner.strip()}")
return "\n".join(items) + "\n"
# --- Blockquote ---
if tag == "blockquote":
inner = get_text(el, " ").strip()
lines = inner.split("\n")
return "\n".join(f"> {line}" for line in lines) + "\n"
# --- Horizontal rule ---
if tag == "hr":
return "---\n"
# --- Code (standalone) ---
if tag == "code":
text = get_text(el, "\n")
return f"`{text}`\n"
# --- Block containers: recurse ---
if tag in ("div", "section", "article", "figure", "figcaption",
"dl", "dd", "dt", "details", "summary", "main", "aside"):
parts = []
for child in el.children:
if isinstance(child, NavigableString):
text = str(child).strip()
if text:
parts.append(text)
elif isinstance(child, Tag):
md = element_to_md(child, pics_dir, session, base_url, img_cache)
if md:
parts.append(md)
return "\n".join(parts)
# --- Unknown tag ---
inner = "".join(inline_to_md(c) for c in el.children)
return inner.strip() if inner.strip() else ""
def extract_content(html: str, url: str) -> list[PageElement]:
"""Parse HTML and extract the main content elements from #help."""
soup = BeautifulSoup(html, "html.parser")
help_div = soup.find(id="help")
if not help_div:
for sel in ("article", "main", ".article", "#article"):
help_div = soup.select_one(sel)
if help_div:
break
if not help_div:
log.warning("No content container found for %s", url)
return []
start_el = help_div.select_one(CONTENT_START_SELECTOR)
if not start_el:
start_el = help_div.find("h1")
if not start_el:
for child in help_div.children:
if isinstance(child, Tag):
start_el = child
break
if not start_el:
log.warning("No content found in #help for %s", url)
return []
elements: list[PageElement] = []
current: PageElement | None = start_el
while current:
elements.append(current)
current = current.find_next_sibling()
return elements
def elements_to_markdown(
elements: list[PageElement],
pics_dir: Path | None,
session: requests.Session | None,
base_url: str,
) -> str:
"""Convert a list of content elements to a Markdown string."""
img_cache: dict[str, str | None] = {}
parts = []
for el in elements:
md = element_to_md(el, pics_dir, session, base_url, img_cache)
if md:
parts.append(md)
result = "\n".join(parts)
result = re.sub(r"\n{3,}", "\n\n", result)
return result.strip() + "\n"
# Phase 2: convert orchestrator
def convert_one(
html_path: str,
md_path: str,
pics_dir: Path | None,
session: requests.Session | None,
base_url: str,
) -> bool:
"""Read a local HTML file and convert to Markdown. Returns True on success."""
html_file = Path(html_path)
if not html_file.exists():
log.warning("HTML cache miss: %s", html_path)
return False
html = html_file.read_text(encoding="utf-8")
elements = extract_content(html, base_url)
if not elements:
log.warning("No content extracted from %s", html_path)
return False
md_text = elements_to_markdown(elements, pics_dir, session, base_url)
out = Path(md_path)
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(md_text, encoding="utf-8")
return True
def run_convert(
sitemap_path: str,
force: bool = False,
dry_run: bool = False,
) -> None:
"""Phase 2: convert cached HTML to Markdown."""
urls = parse_sitemap(sitemap_path)
root_prefix = _root_prefix_from_sitemap(sitemap_path)
label = _label_from_sitemap(sitemap_path)
md_output_dir = SITEMAP_DEFAULTS.get(sitemap_path, ("", label))[0]
file_map = build_file_map(urls, root_prefix, md_output_dir, label)
if dry_run:
print(f"\n=== Convert dry run: {sitemap_path} ===")
print(f"Total: {len(file_map)} files")
html_cache = HTML_CACHE_ROOT / label
cached = sum(1 for e in file_map if Path(e["html_path"]).exists())
missing = len(file_map) - cached
print(f"Cached HTML: {cached}, Missing: {missing}")
for entry in file_map[:10]:
exists = Path(entry["html_path"]).exists()
mark = "" if exists else ""
print(f" [{entry['index']:4d}] {mark} {entry['html_path']}")
print(f"{entry['file_path']}")
if len(file_map) > 10:
print(f" ... ({len(file_map) - 10} more)")
return
# Progress log
log_path = Path(md_output_dir) / ".convert.log"
processed = _load_log(log_path) if not force else set()
# Image session (only needed if images exist)
img_session: requests.Session | None = None
success = 0
skipped = 0
failed = 0
for entry in file_map:
url = entry["url"]
if url in processed and not force:
skipped += 1
continue
pics_dir = None
if entry["chapter_folder"]:
pics_dir = Path(md_output_dir) / entry["chapter_folder"] / "pics"
# Lazy-init image session only when needed
if img_session is None:
img_session = requests.Session()
img_session.headers.update({"User-Agent": get_user_agent(entry["index"])})
base_url = url.rsplit("/", 1)[0] + "/" if "/" in url else url
ok = convert_one(
entry["html_path"], entry["file_path"],
pics_dir, img_session, base_url,
)
if ok:
success += 1
_append_log(log_path, url)
log.info("[%d/%d] OK: %s", success + skipped + failed, len(file_map), url)
else:
failed += 1
log.error("[%d/%d] FAIL: %s", success + skipped + failed, len(file_map), url)
log.info(
"Done: %d success, %d skipped, %d failed (total %d)",
success, skipped, failed, len(file_map),
)
# ---------------------------------------------------------------------------
# Debug
# ---------------------------------------------------------------------------
def run_debug(url: str) -> None:
"""Debug: fetch a single URL, print HTML structure, extract + convert."""
session = requests.Session()
session.headers.update({"User-Agent": get_user_agent()})
print(f"Fetching: {url}")
html = fetch_html(url, session)
if html is None:
print("FETCH FAILED")
return
soup = BeautifulSoup(html, "html.parser")
help_div = soup.find(id="help")
if not help_div:
print("#help NOT FOUND")
return
print("#help found")
h1 = help_div.select_one(CONTENT_START_SELECTOR)
if not h1:
h1 = help_div.find("h1")
if h1:
print(f"Start h1: {h1.get_text(strip=True)[:80]}")
tables = help_div.find_all("table")
imgs = help_div.find_all("img")
pres = help_div.find_all("pre")
ps = help_div.find_all("p")
print(f"Tables: {len(tables)}, Imgs: {len(imgs)}, Pre: {len(pres)}, P: {len(ps)}")
print("\n--- Tables ---")
for i, t in enumerate(tables):
thead = t.find("thead")
rows = t.find_all("tr")
first_cell = rows[0].find(["td", "th"]) if rows else None
cell_text = first_cell.get_text(strip=True)[:60] if first_cell else "(empty)"
has_code = bool(t.find("code") or t.find("pre"))
print(f" Table {i}: thead={bool(thead)}, rows={len(rows)}, "
f"code={has_code}, first: {cell_text}")
print("\n--- Images ---")
for i, img in enumerate(imgs[:10]):
src = img.get("src", "")
alt = img.get("alt", "")
next_p = img.find_next_sibling("p")
next_p_text = next_p.get_text(strip=True)[:60] if next_p else "(no next p)"
print(f" Img {i}: src={src[:60]}, alt={alt[:40]}, next_p: {next_p_text}")
print("\n--- Extraction ---")
elements = extract_content(html, url)
print(f"Extracted {len(elements)} elements")
base_url = url.rsplit("/", 1)[0] + "/" if "/" in url else url
md_text = elements_to_markdown(elements, None, session, base_url)
print(f"\n--- Markdown output ({len(md_text)} chars) ---")
print(md_text[:3000])
if len(md_text) > 3000:
print(f"\n... ({len(md_text) - 3000} more chars)")
# ---------------------------------------------------------------------------
# Progress log helpers
# ---------------------------------------------------------------------------
def _load_log(log_path: Path) -> set[str]:
"""Load set of already-processed URLs from log file."""
if not log_path.exists():
return set()
urls = set()
for line in log_path.read_text().splitlines():
line = line.strip()
if line and not line.startswith("#"):
urls.add(line)
return urls
def _append_log(log_path: Path, url: str) -> None:
"""Append a URL to the processing log."""
log_path.parent.mkdir(parents=True, exist_ok=True)
with open(log_path, "a") as f:
f.write(url + "\n")
# ---------------------------------------------------------------------------
# Sitemap helpers
# ---------------------------------------------------------------------------
def _root_prefix_from_sitemap(sitemap_path: str) -> str:
if "book" in sitemap_path:
return "/en/book/"
if "docs" in sitemap_path:
return "/en/docs/"
return "/"
def _label_from_sitemap(sitemap_path: str) -> str:
if "book" in sitemap_path:
return "book"
if "docs" in sitemap_path:
return "docs"
return "index"
# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------
def main() -> None:
parser = argparse.ArgumentParser(
description="Two-phase MQL5 extraction: download HTML → convert to Markdown."
)
sub = parser.add_subparsers(dest="command", required=True)
# --- download ---
dl = sub.add_parser("download", help="Phase 1: download HTML from mql5.com")
dl_group = dl.add_mutually_exclusive_group(required=True)
dl_group.add_argument("--sitemap", help="Path to a sitemap XML file")
dl_group.add_argument("--all", action="store_true", help="All known sitemaps")
dl.add_argument("--force", action="store_true", help="Re-download all")
dl.add_argument("--dry-run", action="store_true", help="Show plan without fetching")
dl.add_argument("--random-ua", action="store_true",
help="Use random User-Agent per request (default: deterministic rotation)")
dl.add_argument("--random-delay", action="store_true",
help="Random delay between requests (1×–3× base delay)")
dl.add_argument("--user-agents-csv", metavar="CSV",
help="Import User-Agents from CSV file (must have 'user_agent' column)")
# --- convert ---
cv = sub.add_parser("convert", help="Phase 2: convert cached HTML to Markdown")
cv_group = cv.add_mutually_exclusive_group(required=True)
cv_group.add_argument("--sitemap", help="Path to a sitemap XML file")
cv_group.add_argument("--all", action="store_true", help="All known sitemaps")
cv.add_argument("--force", action="store_true", help="Re-convert all")
cv.add_argument("--dry-run", action="store_true", help="Show plan without converting")
# --- debug ---
dbg = sub.add_parser("debug", help="Debug: fetch + analyze a single URL")
dbg.add_argument("url", help="URL to debug")
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose logging")
args = parser.parse_args()
level = logging.DEBUG if args.verbose else logging.INFO
logging.basicConfig(
level=level,
format="%(asctime)s %(levelname)s %(message)s",
datefmt="%H:%M:%S",
)
if args.command == "debug":
run_debug(args.url)
return
targets = []
if args.command == "download":
if args.all:
targets = list(SITEMAP_DEFAULTS.keys())
else:
targets = [args.sitemap]
for st in targets:
if not os.path.exists(st):
log.warning("Sitemap not found: %s — skipping", st)
continue
run_download(st, force=args.force, dry_run=args.dry_run,
random_ua=args.random_ua, random_delay=args.random_delay,
user_agents_csv=args.user_agents_csv)
elif args.command == "convert":
if args.all:
targets = list(SITEMAP_DEFAULTS.keys())
else:
targets = [args.sitemap]
for st in targets:
if not os.path.exists(st):
log.warning("Sitemap not found: %s — skipping", st)
continue
run_convert(st, force=args.force, dry_run=args.dry_run)
if __name__ == "__main__":
main()
+116
View File
@@ -0,0 +1,116 @@
Sitemap: https://www.mql5.com/sitemap.xml
Host: https://www.mql5.com
User-agent: Mail.RU_Bot
Disallow: /
User-agent: MJ12bot
Disallow: /
User-agent: ExaBot
Disallow: /
User-agent: CrazyWebCrawler-Spider
Disallow: /
User-agent: SemrushBot
Disallow: /
User-agent: AhrefsBot
Disallow: /
User-agent: BLEXBot
Disallow: /
User-agent: Yandex
Disallow: /*/search*
Disallow: /data*
Disallow: /return
Disallow: /drawtext
Disallow: /signals/charts/risks
Disallow: /*/signals/*/deals/page*
Disallow: /*/signals/*/pending-orders/page*
Disallow: /*/signals/*/positions/page*
Disallow: /*/signals/new
Disallow: /*/signals/*/news?skip=
Disallow: /*/signals/*/reviews
Disallow: /*/users/*/publications
Disallow: /*/users/*/achievements
Disallow: /*/users/*/feedbacks
Allow: /en/users/*/publications
Disallow: /*/messages/*
Disallow: /messages/avatar
Disallow: /*/channels/*
Disallow: /*/code/viewcode/*
Disallow: /go*
Disallow: /click*
Disallow: /*/auth_set
Disallow: /*/auth_forgotten
Disallow: /*/auth_forgot_confirm
Disallow: /*/publish/*
Disallow: /*_escaped_fragment_
Disallow: /*?print=
Disallow: /p/
Disallow: /captcha/
Disallow: /*/accounting/buy/market/*
Disallow: /*/code/download/*/
Disallow: /*/hosting/rules
Disallow: /maintenance.html
Disallow: /*/market/product/*/comments
Disallow: /*/market/product/*/updates
Disallow: /*/charts/*
Disallow: /*/quotes/charts/*
Disallow: /*/quotes/*/chart
Disallow: /en/quotes/*/chart
Allow: /*/book/applications/charts/
Allow: /*/quotes/widgets/chart
Clean-Param: v
Clean-Param: dir&orderby
Clean-Param: ref&count&Sort&sort
Clean-Param: utm_source&utm_medium&utm_campaign&utm_term&utm_content&utm_nooverride&utm_expid&gclid&_openstat&yclid&gsaid&source&ysclid&clickid&returnUrl&amp
Clean-Param: added&block&pos&device&etext&from
Clean-Param: filter&Filter
User-agent: *
Disallow: /*/search*
Disallow: /data*
Disallow: /return
Disallow: /drawtext
Disallow: /signals/charts/risks
Disallow: /*/signals/*/deals/page*
Disallow: /*/signals/*/pending-orders/page*
Disallow: /*/signals/*/positions/page*
Disallow: /*/signals/new
Disallow: /*/signals/*/news?skip=
Disallow: /*/signals/*/reviews
Disallow: /*/users/*/publications
Disallow: /*/users/*/achievements
Disallow: /*/users/*/feedbacks
Allow: /en/users/*/publications
Disallow: /*/messages/*
Disallow: /messages/avatar
Disallow: /*/channels/*
Disallow: /*/code/viewcode/*
Disallow: /go*
Disallow: /click*
Disallow: /*/auth_set
Disallow: /*/auth_forgotten
Disallow: /*/auth_forgot_confirm
Disallow: /*/publish/*
Disallow: /*_escaped_fragment_
Disallow: /*?print=
Disallow: /p/
Disallow: /captcha/
Disallow: /*/accounting/buy/market/*
Disallow: /*/code/download/*/
Disallow: /*/hosting/rules
Disallow: /maintenance.html
Disallow: /*/market/product/*/comments
Disallow: /*/market/product/*/updates
Disallow: /*/charts/*
Disallow: /*/quotes/charts/*
Disallow: /*/quotes/*/chart
Disallow: /en/quotes/*/chart
Allow: /*/book/applications/charts/
Allow: /*/quotes/widgets/chart
+584
View File
@@ -0,0 +1,584 @@
<?xml version="1.0" encoding="utf-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
<url><loc>https://www.mql5.com/en/book</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>weekly</changefreq><priority>0.6</priority></url>
<url><loc>https://www.mql5.com/en/book/intro</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/intro/edit_compile_run</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/intro/mql_wizard</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/intro/statement_blocks</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/intro/first_program</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/intro/types_and_values</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/intro/variables_and_identifiers</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/intro/init_assign_express</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/intro/data_input</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/intro/errors_debug</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/intro/a_data_output</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/intro/b_formatting</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/intro/c_summing_up</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/identifiers</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/builtin_types</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/builtin_types/integer_numbers</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/builtin_types/float_numbers</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/builtin_types/characters</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/builtin_types/strings</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/builtin_types/booleans</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/builtin_types/datetime</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/builtin_types/colors</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/builtin_types/enums</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/builtin_types/user_enums</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/builtin_types/void</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/variables</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/variables/define_vs_declare</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/variables/scope_and_lifetime</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/variables/initialization</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/variables/static_variables</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/variables/const_variables</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/variables/input_variables</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/variables/variables_extern</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/arrays</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/arrays/arrays_overview</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/arrays/arrays_declaration</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/arrays/arrays_usage</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/expressions</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/expressions/expressions_overview</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/expressions/operator_assignment</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/expressions/operators_arithmetic</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/expressions/increment_decrement</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/expressions/operators_relational</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/expressions/operators_logical</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/expressions/operators_bitwise</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/expressions/operators_compound</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/expressions/operator_conditional</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/expressions/operator_comma</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/expressions/operators_sizeof_typename</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/expressions/operators_parentheses</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/expressions/operators_precedence</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/conversion</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/conversion/conversion_implicit</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/conversion/conversion_arithmetic</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/conversion/conversion_explicit</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/statements</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/statements/statements_compound</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/statements/statements_declaration</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/statements/statements_expression</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/statements/statements_control</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/statements/statements_for</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/statements/statements_while</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/statements/statements_do</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/statements/statements_if</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/statements/statements_switch</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/statements/statements_break</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/statements/statements_continue</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/statements/statements_return</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/statements/statements_null</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/functions</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/functions/functions_definition</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/functions/functions_call</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/functions/functions_parameters</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/functions/functions_ref_value</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/functions/functions_parameters_default</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/functions/functions_return</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/functions/functions_declaration</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/functions/functions_recursive</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/functions/functions_overloading</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/functions/functions_typedef</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/functions/functions_inline</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/preprocessor</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/preprocessor/preprocessor_include</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/preprocessor/preprocessor_define_overview</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/preprocessor/preprocessor_define_simple</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/preprocessor/preprocessor_define_functional</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/preprocessor/preprocessor_sharp</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/preprocessor/preprocessor_undef</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/preprocessor/preprocessor_predefined</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/preprocessor/preprocessor_ifdefs</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/basis/preprocessor/preprocessor_properties</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/structs_and_unions</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/structs_and_unions/structs_definition</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/structs_and_unions/structs_methods</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/structs_and_unions/structs_assignment</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/structs_and_unions/structs_ctor_dtor</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/structs_and_unions/structs_pack_dll</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/structs_and_unions/structs_composition</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/structs_and_unions/structs_access</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/structs_and_unions/unions</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_abstraction</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_encapsulation</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_oop_inheritance</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_polymorphism</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_composition</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_definition</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_access_rights</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_ctors</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_dtors</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_this</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_inheritance</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_new_delete_pointers</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_pointers</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_virtual_override</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_static</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_namespace_context</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_declaration_definition</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_abstract_interfaces</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_operator_overloading</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_dynamic_cast_void</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_ref_pointers_const</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/classes_and_interfaces/classes_final_delete</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/templates</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/templates/templates_header</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/templates/templates_principles</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/templates/templates_vs_macro</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/templates/templates_for_standard_and_object_types</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/templates/templates_functions</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/templates/templates_objects</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/templates/templates_methods</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/templates/templates_nested</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/oop/templates/templates_specialization</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/conversions</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/conversions/conversions_numbers</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/conversions/conversions_normalize</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/conversions/conversions_datetime</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/conversions/conversions_color</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/conversions/conversions_structs</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/conversions/conversions_enums</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/conversions/conversions_complex</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/strings</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/strings/strings_init</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/strings/strings_concatenation</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/strings/strings_comparison</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/strings/strings_case_trim</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/strings/strings_find_replace_split</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/strings/strings_codepages</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/strings/strings_format</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/arrays</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/arrays/arrays_print</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/arrays/arrays_dynamic</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/arrays/arrays_metrics</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/arrays/arrays_init_fill</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/arrays/arrays_edit</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/arrays/arrays_move_swap</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/arrays/arrays_compare_sort_search</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/arrays/arrays_as_series</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/arrays/zero_memory</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/maths</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/maths/maths_abs</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/maths/maths_max_min</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/maths/maths_rounding</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/maths/maths_mod</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/maths/maths_pow_sqrt</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/maths/maths_exp_log</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/maths/maths_trig</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/maths/maths_hyper</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/maths/maths_nan</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/maths/maths_rand</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/maths/maths_byte_swap</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/files</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/files/files_modes_bin_txt</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/files/files_save_load</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/files/files_open_close</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/files/files_handles</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/files/files_txt_codepage</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/files/files_arrays</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/files/files_bin_structs</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/files/files_bin_atomic</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/files/files_txt_atomic</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/files/files_cursor</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/files/files_properties</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/files/files_flush</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/files/files_exist_delete</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/files/files_copy_move</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/files/files_find</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/files/files_folders</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/files/files_select</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/globals</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/globals/globals_set_get</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/globals/globals_exist_time</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/globals/globals_list</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/globals/globals_delete</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/globals/globals_temp</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/globals/globals_condition</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/globals/globals_flush</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/timing</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/timing/timing_local_server</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/timing/timing_daylight_saving</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/timing/timing_gmt</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/timing/timing_sleep</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/timing/timing_count</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/output</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/output/output_print</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/output/output_alert</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/output/output_comment</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/output/output_messagebox</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/output/output_sound</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment/env_listing</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment/env_build</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment/env_type_license</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment/env_mode</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment/env_permissions</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment/env_connectivity</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment/env_resources</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment/env_screen</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment/env_descriptive</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment/env_bar_lang</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment/env_signature</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment/env_keyboard</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment/env_stop</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment/env_terminal_close</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment/env_last_error</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment/env_user_error</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment/env_debug_break</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment/env_variables</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/environment/env_constants</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/matrices</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/matrices/matrices_types</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/matrices/matrices_init</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/matrices/matrices_copy</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/matrices/matrices_copyrates</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/matrices/matrices_copyticks</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/matrices/matrices_expressions</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/matrices/matrices_manipulations</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/matrices/matrices_mul</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/matrices/matrices_decomposition</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/matrices/matrices_stats</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/matrices/matrices_characteristics</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/matrices/matrices_sle</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/common/matrices/matrices_ml</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/runtime</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/runtime/runtime_features_by_progtype</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/runtime/runtime_threads</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/runtime/runtime_events_overview</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/runtime/runtime_lifecycle</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/runtime/runtime_oninit_ondeinit</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/runtime/runtime_onstart</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/runtime/runtime_remove</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/script_service</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/script_service/scripts</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/script_service/services</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/script_service/script_service_limitations</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/timeseries</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/timeseries/timeseries_symbol_period</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/timeseries/timeseries_storage_tech</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/timeseries/timeseries_properties</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/timeseries/timeseries_bars</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/timeseries/timeseries_ibarshift</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/timeseries/timeseries_copy_funcs_overview</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/timeseries/timeseries_mqlrates</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/timeseries/timeseries_ohlcvs</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/timeseries/timeseries_single_value</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/timeseries/timeseries_highest_lowest</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/timeseries/timeseries_ticks_mqltick</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_features</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_oncalculate</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_window_chart_separate</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_buffers_plots</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_setindexbuffer</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_plotindexsetinteger</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_buffer_to_plot_mapping</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_properties</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_labels</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_empty_value</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_separate_window</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_caption_digits</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_color</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_begin</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_wait_none</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_multisymbol</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_newbars</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_test</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_limitations</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_make/indicators_wizard</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_use</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_use/indicators_descriptors</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_use/indicators_icustom</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_use/indicators_barscalculated</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_use/indicators_copybuffer</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_use/indicators_multitimeframe</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_use/indicators_standard</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_use/indicators_standard_use</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_use/indicators_indicatorcreate</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_use/indicators_flexible_create</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_use/indicators_chart_review</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_use/indicators_chart_plus_subwindow</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_use/indicators_shifted</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_use/indicators_indicatorrelease</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_use/indicators_parameters</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/indicators_use/indicators_apply_to</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/timer</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/timer/timer_event_set</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/timer/timer_ontimer</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/timer/timer_event_set_millisecond</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_main_properties</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_id</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_list</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_symbol_period</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_properties_overview</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_string_properties</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_window_state</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_count_visibility</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_mode</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_show_elements</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_shift</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_scale_time</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_scale_price</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_color</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_keyboard_mouse</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_floating</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_on_drop</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_coordinates</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_navigate</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_redraw</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_set_symbol_period</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_indicators</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_open_close</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_tpl</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/charts/charts_screenshot</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_main_characteristics</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_time_price</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_screen_coordinates</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_create</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_delete</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_find</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_properties_get_set</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_properties_main</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_time_price_coordinates</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_corner_x_y</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_anchor</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_state</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_z_order</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_color_style</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_font</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_angle</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_width_height</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_timeframes</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_arrow_codes</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_rays</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_pressed_state</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_bitmap</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_bitmap_offset</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_edit</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_stddev_channel</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_levels</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_gann_fibo_elliott</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_chart</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_move</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/objects/objects_get_time_value</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/events</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/events/events_onchartevent</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/events/events_properties</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/events/events_chart</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/events/events_keyboard</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/events/events_mouse</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/events/events_objects</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/applications/events/events_custom</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_list</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_select</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_exist</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_sync</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_tick</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_sessions</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_margin_rates</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_info</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_state</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_chart_mode</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_currencies</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_point_tick</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_volume</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_trade_mode</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_execution_filling</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_margin</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_expiration</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_spreads_levels</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_swaps</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_tick_parts</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_description</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_market_depth</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_custom</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/symbols/symbols_special</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/marketbook</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/marketbook/marketbook_add_release</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/marketbook/marketbook_events</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/marketbook/marketbook_get</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/marketbook/marketbook_application</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/account</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/account/account_info_overview</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/account/account_number_identity</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/account/account_real_demo_contest</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/account/account_currency</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/account/account_netting_hedge</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/account/account_limits_and_restrictions</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/account/account_margin</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/account/account_state</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_ontick</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_order_deal_position</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_request_types</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_order_type</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_execution_filling</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_pending_expiration</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_ordercalcmargin</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_ordercalcprofit</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_mqltraderequest</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_mqltradecheckresult</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_ordercheck</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_mqltraderesult</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_ordersend_ordersendasync</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_market_buy_sell</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_modify_position</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_trailing_stop</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_close</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_closeby</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_pending</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_modify_order</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_remove_order</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_order_list</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_order_properties</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_orderget_funcs</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_order_filter</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_position_list</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_position_properties</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_positionget_funcs</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_deal_properties</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_history_select</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_historyorderget_funcs</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_historydealget_funcs</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_transaction_type</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_ontradetransaction</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_sync_vs_async</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_ontrade</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_trade_state</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_multisymbol</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_limitations</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/experts/experts_wizard</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester/tester_ticks</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester/tester_time</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester/tester_chart_limits</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester/tester_multicurrency_sync</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester/tester_criterion</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester/tester_testerstatistics</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester/tester_ontester</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester/tester_parameterrange</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester/tester_ontester_init_pass_deinit</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester/tester_frameadd</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester/tester_framenext</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester/tester_directives</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester/tester_testerhideindicators</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester/tester_withdraw_deposit</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester/tester_testerstop</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester/tester_example_ea</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester/tester_math_calc</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester/tester_debug_profile</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/automation/tester/tester_limitations</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/resources</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/resources/resources_directive</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/resources/resources_sharing</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/resources/resources_variables</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/resources/resources_indicators</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/resources/resources_resourcecreate</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/resources/resources_resourcefree</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/resources/resources_resourcereadimage</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/resources/resources_resourcesave</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/resources/resources_textout</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/resources/resources_applied_usecase</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/custom_symbols</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/custom_symbols/custom_symbols_create_delete</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/custom_symbols/custom_symbols_properties</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/custom_symbols/custom_symbols_margin</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/custom_symbols/custom_symbols_sessions</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/custom_symbols/custom_symbols_rates</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/custom_symbols/custom_symbols_ticks</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/custom_symbols/custom_symbols_market_book</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/custom_symbols/custom_symbols_trade_specifics</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/calendar</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/calendar/calendar_overview</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/calendar/calendar_countries</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/calendar/calendar_event_kinds_by_country_currency</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/calendar/calendar_event_kind_by_id</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/calendar/calendar_records_by_country_currency</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/calendar/calendar_records_by_event_kind</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/calendar/calendar_record_by_id</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/calendar/calendar_change_last</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/calendar/calendar_change_last_by_event</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/calendar/calendar_filter_custom</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/calendar/calendar_cache_tester</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/calendar/calendar_trading</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/crypt</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/crypt/crypt_overview</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/crypt/crypt_encode</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/crypt/crypt_decode</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/network</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/network/network_push</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/network/network_email</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/network/network_ftp</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/network/network_http</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/network/network_socket_create_connect</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/network/network_socket_state</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/network/network_socket_timeouts</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/network/network_socket_send_read</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/network/network_socket_tls_handshake_cert</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/network/network_socket_tls_send_read</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/sqlite</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/sqlite/sqlite_intro</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/sqlite/sqlite_overview</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/sqlite/sqlite_scheme_types</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/sqlite/sqlite_orm</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/sqlite/sqlite_db_create_open_close</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/sqlite/sqlite_simple_queries</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/sqlite/sqlite_table_exists</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/sqlite/sqlite_prepare</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/sqlite/sqlite_reset</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/sqlite/sqlite_bind</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/sqlite/sqlite_read</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/sqlite/sqlite_columns</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/sqlite/sqlite_crud_examples</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/sqlite/sqlite_transactions</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/sqlite/sqlite_export_import</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/sqlite/sqlite_print</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/sqlite/sqlite_example_ts</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/libraries</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/libraries/libraries_export</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/libraries/libraries_import</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/libraries/libraries_path_lookup</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/libraries/libraries_dll</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/libraries/libraries_class_template</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/libraries/libraries_dotnet</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/project</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/project/project_mqproj</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/project/project_web_service_plan</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/project/project_nodejs</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/project/project_websockets</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/project/project_websocket_server</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/project/project_websocket_mql5</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/project/project_echo_chat_mql5</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/project/project_trade_signal_server</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/project/project_trade_signal_client_mql5</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/python</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/python/python_install</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/python/python_funcs_overview</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/python/python_init</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/python/python_last_error</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/python/python_account_info</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/python/python_terminal_info</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/python/python_symbols</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/python/python_marketbook</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/python/python_copyrates</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/python/python_copyticks</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/python/python_margin_profit</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/python/python_ordercheck_ordersend</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/python/python_orders</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/python/python_positions</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/python/python_history_deals</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/advanced/opencl</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
<url><loc>https://www.mql5.com/en/book/conclusion</loc><lastmod>2026-01-20T09:47:16+00:00</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
</urlset>
File diff suppressed because it is too large Load Diff
+505
View File
@@ -0,0 +1,505 @@
---
name: mql5
description: >
MQL5 development skill for MetaTrader 5 Expert Advisors, Indicators, Scripts,
and Services. Focus on positions, orders, indicators, ticks, bars, risk
management, backtesting, and multi-instance MT5 operations. Includes
programming book and API reference documentation.
version: "0.1"
license: MIT
compatibility: >
Target: MetaTrader 5 platform. Language: MQL5 (C++-like syntax).
File extensions: *.mq5 (source), *.mqh (headers).
Run time: Windows native, Linux via Wine, macOS via Wine.
metadata:
project-version: "0.1.0"
focus-areas:
- positions
- orders
- indicators
- ticks
- bars
- risk-management
- backtesting
---
# MQL5 Development Skill
Expert development skill for MetaTrader 5. Covers EA, Indicator, Script, and
Service creation with emphasis on trading operations, technical indicators,
multi-timeframe analysis, risk management, and backtesting workflows.
## 1. MQL5 Fundamentals
### Language and File Types
- MQL5 syntax is similar to C++ but with domain-specific additions
- Source files: `*.mq5` (programs), `*.mqh` (headers)
- Compiled output: `*.ex5` (same name as source)
- Compiler: built into MetaEditor IDE
### Program Types
| Type | Purpose | Key Handler | Directory |
|------|---------|-------------|-----------|
| Expert Advisor | Automated trading | `OnTick()` | `MQL5/Experts/` |
| Indicator | Technical analysis | `OnCalculate()` | `MQL5/Indicators/` |
| Script | One-shot execution | `OnStart()` | `MQL5/Scripts/` |
| Service | Background task | `OnStart()` + `OnTimer()` | `MQL5/Services/` |
### MQL5 Directory Structure
Default locations per platform:
| Platform | Path |
|----------|------|
| Windows 10+ | `$env:USERPROFILE\AppData\Roaming\MetaQuotes\Terminal\$INSTANT_HEX\MQL5` |
| Linux (Wine) | `~/.wine/drive_c/Program Files/MetaTrader 5/MQL5/` |
| macOS | Unknown — verify per installation |
Key subdirectories:
```
MQL5/
├── Experts/ # EA source files (.mq5)
│ ├── Examples/ # Built-in example EAs
│ └── Free Robots/ # Downloaded EAs
├── Indicators/ # Indicator source files
├── Scripts/ # Script source files
├── Services/ # Service source files
├── Include/ # Header files (.mqh)
│ ├── Trade/ # Trading classes (Trade.mqh, PositionInfo.mqh, etc.)
│ ├── Indicators/ # Indicator helpers
│ ├── Expert/ # Expert base classes
│ └── Generic/ # Generic collections
├── Files/ # File I/O sandbox
├── Images/ # Image resources
├── Libraries/ # DLL/shared libraries
├── Profiles/ # Chart profiles
└── Logs/ # Log files
```
### Multi-Instance MT5
Multiple MT5 instances can run simultaneously for different accounts:
1. Install MT5 to separate target paths (e.g. `MT5_BrokerA/`, `MT5_BrokerB/`)
2. Each instance has its own `MQL5/` directory
3. To identify which account an instance is logged into:
- `AccountInfoInteger(ACCOUNT_LOGIN)` — account number
- `AccountInfoString(ACCOUNT_NAME)` — account name
- `AccountInfoString(ACCOUNT_SERVER)` — broker server
4. Each instance runs as a separate process — use `Magic Number` to distinguish
EA trades across instances on the same symbol
## 2. Trading Operations
### Core Concepts
- **Order**: instruction to buy/sell (Market or Pending)
- **Deal**: executed exchange (buy at Ask, sell at Bid)
- **Position**: current obligation (long or short)
### CTrade Class (Standard Library)
```mql5
#include <Trade\Trade.mqh>
CTrade trade;
// Setup in OnInit()
trade.SetExpertMagicNumber(EA_MAGIC);
trade.SetMarginMode();
trade.SetTypeFillingBySymbol(Symbol());
trade.SetDeviationInPoints(Slippage);
```
Key methods:
| Method | Purpose |
|--------|---------|
| `PositionOpen(symbol, type, volume, price, sl, tp)` | Open a position |
| `PositionClose(symbol, deviation)` | Close a position |
| `PositionModify(symbol, sl, tp)` | Modify SL/TP |
| `PositionClosePartial(symbol, volume)` | Partial close |
| `Buy(volume, price, sl, tp, comment)` | Shortcut for buy |
| `Sell(volume, price, sl, tp, comment)` | Shortcut for sell |
| `BuyLimit/BuyStop/SellLimit/SellStop(...)` | Pending orders |
| `ResultRetcode()` | Check trade server return code |
| `ResultDeal()` | Get deal ticket after execution |
### Position Queries
```mql5
// Iterate open positions (Hedging account)
uint total = PositionsTotal();
for (uint i = 0; i < total; i++) {
string sym = PositionGetSymbol(i);
if (sym == _Symbol && PositionGetInteger(POSITION_MAGIC) == EA_MAGIC) {
double vol = PositionGetDouble(POSITION_VOLUME);
double sl = PositionGetDouble(POSITION_SL);
double tp = PositionGetDouble(POSITION_TP);
long type = PositionGetInteger(POSITION_TYPE);
}
}
// Netting account — simpler
if (PositionSelect(_Symbol)) {
// position is selected
}
```
### Order Execution Pattern
```mql5
// Calculate price
double price = (signal == ORDER_TYPE_BUY)
? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
: SymbolInfoDouble(_Symbol, SYMBOL_BID);
// Open with SL/TP
trade.PositionOpen(_Symbol, signal, lotSize, price, sl, tp, "EA Signal");
// Always check result
if (trade.ResultRetcode() != TRADE_RETCODE_DONE) {
Print("Trade failed: ", trade.ResultRetcode());
}
```
### Hedging vs Netting
```mql5
bool IsHedging = ((ENUM_ACCOUNT_MARGIN_MODE)
AccountInfoInteger(ACCOUNT_MARGIN_MODE) == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
```
- **Hedging**: multiple positions per symbol, must iterate and match Magic Number
- **Netting**: one position per symbol, use `PositionSelect()`
## 3. Indicators and Multi-Timeframe
### Built-in Indicator Handles
```mql5
// Moving Average
int handle = iMA(_Symbol, PERIOD_H1, 50, 0, MODE_SMA, PRICE_CLOSE);
// RSI
int handle = iRSI(_Symbol, PERIOD_H1, 14, PRICE_CLOSE);
// MACD
int handle = iMACD(_Symbol, PERIOD_H1, 12, 26, 9, PRICE_CLOSE);
// Bollinger Bands
int handle = iBands(_Symbol, PERIOD_H1, 20, 0, 2.0, PRICE_CLOSE);
```
### Reading Indicator Values
```mql5
double buffer[];
ArraySetAsSeries(buffer, true);
if (CopyBuffer(handle, 0, 0, 3, buffer) != 3) {
Print("No indicator data");
return;
}
// buffer[0] = current bar value
// buffer[1] = previous bar value
```
### Multi-Timeframe Analysis
```mql5
// Higher timeframe trend
int h4_ma = iMA(_Symbol, PERIOD_H4, 50, 0, MODE_SMA, PRICE_CLOSE);
// Entry timeframe signal
int h1_rsi = iRSI(_Symbol, PERIOD_H1, 14, PRICE_CLOSE);
// In OnTick():
double h4_val[], h1_val[];
CopyBuffer(h4_ma, 0, 0, 1, h4_val);
CopyBuffer(h1_rsi, 0, 0, 1, h1_val);
bool bullish = (SymbolInfoDouble(_Symbol, SYMBOL_BID) > h4_val[0]);
bool oversold = (h1_val[0] < 30);
```
### New Bar Detection
```mql5
datetime lastBarTime = 0;
void OnTick() {
datetime currentBarTime = iTime(_Symbol, _Period, 0);
if (currentBarTime == lastBarTime) return; // not a new bar
lastBarTime = currentBarTime;
// New bar — run analysis here
}
```
## 4. Ticks and Bars
### Timeseries Access
Index 0 = current (unfinished) bar. Array is reverse-ordered.
```mql5
MqlRates rates[];
ArraySetAsSeries(rates, true);
CopyRates(_Symbol, _Period, 0, 100, rates);
// rates[0] = current bar
// rates[1] = previous bar
// rates[0].open, .high, .low, .close, .tick_volume, .time
```
### Tick Data
```mql5
MqlTick tick;
SymbolInfoTick(_Symbol, tick);
// tick.bid, tick.ask, tick.last, tick.volume, tick.time
```
### Key Functions
| Function | Purpose |
|----------|---------|
| `CopyRates()` | Bulk OHLCV data |
| `CopyOpen/High/Low/Close()` | Individual price arrays |
| `CopyTime()` | Bar open times |
| `CopyBuffer()` | Indicator buffer values |
| `iBars()` | Bar count for symbol/period |
| `iBarShift()` | Bar index by time |
| `iTime()` | Bar open time by shift |
| `SymbolInfoTick()` | Current tick data |
## 5. Risk Management and Lot Sizing
### Fixed Percentage Risk
```mql5
double CalculateLotSize(double riskPercent, double slPoints) {
double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskAmount = accountBalance * riskPercent / 100.0;
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
if (tickValue == 0 || tickSize == 0 || slPoints == 0) return 0;
double slMoneyPerLot = (slPoints * point / tickSize) * tickValue;
double lot = riskAmount / slMoneyPerLot;
// Normalize to broker constraints
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
lot = MathFloor(lot / lotStep) * lotStep;
lot = MathMax(lot, minLot);
lot = MathMin(lot, maxLot);
return NormalizeDouble(lot, 2);
}
```
### Risk-to-Reward Ratio
```mql5
// Minimum 1:2 RR
double slDistance = MathAbs(price - sl);
double tpDistance = slDistance * 2; // 1:2 minimum
double tp = (orderType == ORDER_TYPE_BUY) ? price + tpDistance : price - tpDistance;
```
### Position Sizing Rules
1. Never risk more than 1-2% per trade
2. Calculate lot size from risk amount and SL distance
3. Normalize to broker's lot step and min/max constraints
4. Account for spread when calculating SL distance
## 6. Backtesting and Optimization
### Strategy Tester
The Strategy Tester is built into MT5. Key concepts:
1. **Single Test**: run EA once with fixed parameters
2. **Optimization**: genetic algorithm searches parameter space
3. **Custom Criterion**: `OnTester()` returns optimization value
### OnTester Handler
```mql5
double OnTester() {
// Called after each test pass
// Return value used as "Custom max" optimization criterion
double profit = TesterStatistics(STAT_PROFIT);
double dd = TesterStatistics(STAT_BALANCE_DDREL_PERCENT);
double trades = TesterStatistics(STAT_TRADES);
double pf = TesterStatistics(STAT_PROFIT_FACTOR);
double sharpe = TesterStatistics(STAT_SHARPE_RATIO);
// Minimum trade count filter
if (trades < 50) return 0;
// Custom criterion: profit factor * (1 - max drawdown%)
return pf * (1.0 - dd / 100.0);
}
```
### Key Statistics
| Stat | Description |
|------|-------------|
| `STAT_PROFIT` | Net profit/loss |
| `STAT_PROFIT_FACTOR` | Gross profit / gross loss |
| `STAT_BALANCE_DDREL_PERCENT` | Max balance drawdown % |
| `STAT_SHARPE_RATIO` | Sharpe ratio |
| `STAT_TRADES` | Number of trades |
| `STAT_PROFIT_TRADES` | Winning trades |
| `STAT_LOSS_TRADES` | Losing trades |
| `STAT_EXPECTED_PAYOFF` | Average profit per trade |
| `STAT_RECOVERY_FACTOR` | Profit / max drawdown |
### Backtesting Workflow
1. Code the EA with `OnTick()`, `OnInit()`, `OnDeinit()`
2. Add `OnTester()` for custom optimization criterion
3. In MT5: Strategy Tester → select EA → set symbol/timeframe/period
4. Choose "Open prices only" for speed, "Every tick" for accuracy
5. Run single test → check results
6. Run optimization → find best parameters
7. Validate with out-of-sample data
### Automated Backtesting Loop
```
EA Development Cycle:
Code → Compile → Single Test → Check Results
If promising → Optimize → Analyze Results
If validated → Forward Test → Deploy
Monitor → Collect Data → Refine → Repeat
```
## 7. Event Handlers Reference
| Handler | When Called | Use Case |
|---------|-----------|----------|
| `OnInit()` | EA/indicator starts | Initialize handles, variables |
| `OnDeinit()` | EA/indicator stops | Cleanup, release handles |
| `OnTick()` | New tick received | EA main logic |
| `OnTimer()` | Timer event | Periodic operations |
| `OnTrade()` | Trade event | React to trade changes |
| `OnTradeTransaction()` | Trade transaction | Detailed trade tracking |
| `OnChartEvent()` | Chart interaction | GUI buttons, objects |
| `OnCalculate()` | Indicator calculation | Indicator main logic |
| `OnTester()` | Test complete | Custom optimization criterion |
| `OnTesterInit()` | Optimization start | Setup for optimization |
| `OnTesterPass()` | Each optimization pass | Log intermediate results |
## 8. Common Pitfalls
1. **Always check `ResultRetcode()`** after `PositionOpen()` — success != execution
2. **Use `SetExpertMagicNumber()`** to distinguish your EA's trades
3. **Normalize prices** with `SymbolInfoInteger(_Symbol, SYMBOL_DIGITS)`
4. **Check `Bars() > N`** before trading to ensure enough history
5. **Use `ArraySetAsSeries(true)`** for timeseries arrays (index 0 = latest)
6. **Release indicator handles** in `OnDeinit()` with `IndicatorRelease()`
7. **Don't trade on `OnInit()`** — wait for first `OnTick()`
8. **Account type matters**: Hedging requires iterating positions, Netting uses select
9. **Spread varies**: use `SymbolInfoInteger(_Symbol, SYMBOL_SPREAD)` for live spread
10. **Timer in tester**: use `EventSetTimer()` in `OnInit()`, not hardcoded delays
## 9. Quick Reference — EA Skeleton
```mql5
//+------------------------------------------------------------------+
//| MyExpertAdvisor.mq5 |
//+------------------------------------------------------------------+
#property copyright "Your Name"
#property link ""
#property version "1.00"
#include <Trade\Trade.mqh>
input double RiskPercent = 1.0; // Risk % per trade
input int Slippage = 10; // Max slippage in points
input int MagicNumber = 12345; // EA magic number
#define EA_MAGIC MagicNumber
CTrade trade;
bool IsHedging;
datetime lastBarTime = 0;
//+------------------------------------------------------------------+
int OnInit() {
IsHedging = ((ENUM_ACCOUNT_MARGIN_MODE)
AccountInfoInteger(ACCOUNT_MARGIN_MODE) == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
trade.SetExpertMagicNumber(EA_MAGIC);
trade.SetMarginMode();
trade.SetTypeFillingBySymbol(Symbol());
trade.SetDeviationInPoints(Slippage);
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
// Cleanup
}
//+------------------------------------------------------------------+
void OnTick() {
// New bar check
datetime barTime = iTime(_Symbol, _Period, 0);
if (barTime == lastBarTime) return;
lastBarTime = barTime;
// Analysis and trading logic here
// ...
}
//+------------------------------------------------------------------+
double OnTester() {
// Custom optimization criterion
double trades = TesterStatistics(STAT_TRADES);
if (trades < 30) return 0;
return TesterStatistics(STAT_PROFIT_FACTOR);
}
```
## 10. References
### In this skill
- `references/book/` — Programming book (learning path, 581 pages)
- `00-intro/` — Introduction and IDE
- `01-basis/` — Language fundamentals
- `02-oop/` — Object-oriented programming
- `03-common/` — Common functions (strings, files, math)
- `04-applications/` — Charts, indicators, objects, events
- `05-automation/` — Trading, symbols, tester
- `06-advanced/` — Resources, SQLite, Python, OpenCL
- `references/docs/` — API reference (4135 pages)
- `19-trading/` — Trading functions (OrderSend, PositionGet, etc.)
- `16-series/` — Timeseries access (CopyRates, CopyBuffer, etc.)
- `26-indicators/` — Built-in indicators (iMA, iRSI, iMACD, etc.)
- `24-customind/` — Custom indicator creation
- `13-event-handlers/` — Event handlers (OnTick, OnTester, etc.)
- `34-standardlibrary/` — Standard library (CTrade, CPositionInfo, etc.)
### External
- [MQL5 Reference](https://www.mql5.com/en/docs)
- [MQL5 Book](https://www.mql5.com/en/book)
- [Strategy Tester Guide](https://www.mql5.com/en/terminal/strategytester)
@@ -0,0 +1,60 @@
# Introduction to MQL5 and development environment
One of the most important changes in MQL5 in its reincarnation in MetaTrader 5 is that it supports the object-oriented programming (OOP) concept. At the time of its appearance, the preceding MQL4 (the language of MetaTrader 4) was conventionally compared to the C programming language, while it is more reasonable to liken MQL5 to C++. In all fairness, it should be noted that today all OOP tools that initially had only been available in MQL5 were transferred into MQL4. However, users who scarcely know programming still perceive OOP as something too complicated.
In a sense, this book is aiming at making complex things simple. It is not to replace, but to be added to the MQL5 Language Reference that is supplied with the terminal and also available on the mql5.com website.
In this book, we are going to consistently tell you about all the components and techniques of programming in MQL5, taking baby steps so that each iteration is clear and the OOP technology gradually unlocks its potential that is especially notable, as with any powerful tool, when it is used properly and reasonably. As a result, the developers of MQL programs will be able to choose a preferred programming style suitable for a specific task, i.e., not only the object-oriented but also the 'old' procedural one, as well as use various combinations of them.
Users of the trading terminal can be conveniently classified into "programmers" (those who have already some experience in programming in at least one language) and "non-programmers" ("pure" traders interested in the customization capacity of the terminal using MQL5). The former ones can optionally skip the first and the second parts of this book describing the basic concepts of language and immediately start learning about the specific APIs (Application Programming Interfaces) embedded in MetaTrader 5. For the latter ones, progressive reading is recommended.
Among the category of "programmers," those knowing C++ have the best advantages, since MQL5 and C++ are similar. However, this "medal" has its reverse side. The matter is that MQL5 does not completely match with C++ (especially when compared to the recent standards). Therefore, attempts to write one structure or another through habit "as on pluses" will frequently be interrupted by unexpected errors of the compiler. Considering specific elements of the language, we will do our best to point out these differences.
Technical analysis, executing trading orders, or integration with external data sources — all these functions are available to the terminal users both from the user interface and via software tools embedded in MQL5.
Since MQL5 programs must perform different functions, there are some specialized program types supported in MetaTrader 5. This is a standard technique in many software systems. For example, in Windows, along with usual windowing programs, there are command-line-driven programs and services.
The following program types are available in MQL5:
- Indicators — programs aimed at graphically displaying data arrays computed by a given formula, normally based on the series of quotes;
- Expert Advisors — programs to automate trading completely or partly;
- Scripts — programs intended for performing one action at a time; and
- Services — programs for performing permanent background actions.
We will discuss the purposes and special features of each type in detail later. It is important to note now that they all are created in MQL5 and have much in common. Therefore, we will start learning with common features and gradually get to know about the specificity of each type.
The essential technical feature of MetaTrader consists in exerting the entire control in the client terminal, while commands initiated in it are sent to the server. In other words, MQL-based applications can only work within the client terminal, most of them requiring a 'live' connection to the server to function properly. No applications are installed on the server. The server just processes the orders received from the client terminal and returns the changes in the trading environment. These changes also become available to MQL5 programs.
Most types of MQL5 programs are executed in the chart context, i.e., to launch a program, you should 'throw' it onto the desired chart. The exception is only a special type, i.e., services: They are intended for background operation, without being attached to the chart.
We recall that all MQL5 programs are inside the working MetaTrader 5 folder, in the nested folder named /MQL5/<type>, where <type> is, respectively:
- Indicators
- Experts
- Scripts
- Services
Based on the MetaTrader 5 installation technique, the path to the working folder can be different (particularly, with the limited user rights in Windows, in a normal mode or portable). For example, it can be:
```
C:/Program Files/MetaTrader 5/
```
or
```
C:/Users/<username>/AppData/Roaming/MetaQuotes/Terminal/<instance_id>/
```
The user can get to know where this folder is located exactly by executing the File -> Open data catalog command (it is available in both terminal and editor). Moreover, when creating a new program, you don't need to think of looking up the correct folder due to using the MQL Wizard embedded in the editor. It is called for by the File -> New command and allows selecting the required type of the MQL5 program. The relevant text file containing a source code template will be created automatically where necessary upon completing the Master and then opened for editing.
In the MQL5 folder, there are other nested folders, along with the above ones, and they are also directly related to MQL5 programming, but we will refer to them later.
```
MQL5 Programming for Traders — Source Codes from the Book. Part 1
Examples from the book are also available in the public project \MQL5\Shared Projects\MQL5Book
```
@@ -0,0 +1,33 @@
# Editing, compiling, and running programs
All MetaTrader 5 programs are compilable. That is, a source code written in MQL5 must be compiled to obtain the binary representation that will be exactly the one executed in the terminal.
Programs are edited and compiled using MetaEditor.
![Editing an MQL program in MetaEditor](pics/me_edit_en.png)
Editing an MQL program in MetaEditor
Source code is a text written according to the MQL5 rules and saved as a file having the extension of mq5. The file containing a compiled program will have the same name, while its extension will be ex5.
In the simplest case, one executable file corresponds with one file containing the source code; however, as we will see later, coding complex programs frequently requires splitting the source code into multiple files: The main one and some supporting ones that are enabled from the main file in a special manner. In this case, the main file must still have the extension of mq5, while those enabled from it must have the extension of mqh. Then statements from all source files will get into the executable file being generated. Thus, multiple files containing the source code may be the starting point for creating one executable file/program. All this mentioned here to complete the picture is going to be presented in the second part of the book.
We will use the term MQL5 syntax to denote the set of all rules that allow constructing programs in MQL5. Only the strict adherence to the syntax allows coding programs compatible with the compiler. In fact, teaching to code consists of sequentially introducing all the rules of a particular language that is MQL5, in our case. And this is the main purpose of this book.
To compile a source code, we can use the command MetaEditor File -> Compile or just press F7. However, there are some other, special methods to compile we will discuss them later. Compiling is accompanied by displaying the changing status in the editor log (where an MQL5 program consists of multiple files containing the source code, and enabling each file is marked in a single log line).
![Compiling an MQL5 program in MetaEditor](pics/me_compile_en.png)
Compiling an MQL5 program in MetaEditor
An indication of a successful compilation is zero errors ("0 errors"). Warnings do not affect the compilation results, they just inform on potential issues. Therefore, it is recommended to fix them on the same basis as errors (we will tell you later how to do that). Ideally, there should not be any warnings ("0 warnings").
Upon the successful compilation of an mq5 file, we get a same-name file with the extension of ex5. MetaTrader 5 Navigator displays as a tree all executable ex5 files located in folder MQL5 and its subfolders, including the one just compiled.
![MetaTrader 5 Navigator with a compiled MQL5 program](pics/mt_navex_en.png)
MetaTrader 5 Navigator with a compiled MQL5 program
Ready programs are launched in the terminal using any methods familiar to the user. For instance, any program, other than a service, can be dragged with the mouse from Navigator to the chart. We will talk about the features of services separately.
Besides, developers often need the program to be executed in the debugging mode to find what causes the errors. There are multiple special commands for this purpose, and we will refer to them in [Bug fixing and debugging](/en/book/intro/errors_debug).
@@ -0,0 +1,55 @@
# MQL Wizard and program draft
Here we will consider the simplest MQL program that does not really do anything. It is aimed at introducing the process of writing a source code in the editor, compiling it, and launching it in the terminal. Following the steps below independently, you will make sure that programming is available to casual users and start adapting to the integrated development environment of MQL5 programs. It will always be needed for consolidating the material covered.
The simplest MQL5 programs are scripts. Therefore, it is a script that we are going to try and create. For this purpose, let's start MQL5 Wizard (File -> New). In the first step, we will select Script in the list of types and press Next:
![Creating a script using MQL Wizard. Step 1](pics/wizard_start_en.png)
Creating a script using MQL Wizard. Step 1
In the second step, we will introduce the script name in the Name field, having added it after the default folder mentioned above and a backslash: "Scripts\". For instance, let's name the script "Hello" (that is, the Name field will contain the line: "Scripts\Hello") and, without changing anything else, press Finish.
![Creating a script using MQL Wizard. Step 2](pics/wizard_script_name_params_en.png)
Creating a script using MQL Wizard. Step 2
As a result, the Wizard will create a file named Hello.mq5 and open it for editing. The file is located in folder MQL5/Scripts (standard location for scripts) because we have used the default folder; however, we could add any sub-folder or even a sub-folder hierarchy. For instance, if we write "Scripts\Exercise\Hello" in the Name field at Wizard Step 1, then the Exercise sub-folder will be created in the Scripts folder automatically, and the file Hello.mq5 will be located in that sub-folder.
All examples from this book will be located in the MQL5Book folders inside catalogs allocated for the MQL programs of relevant types. This is necessary to facilitate installing the examples into your working copy of the terminal and rule out any name conflicts with other MQL programs you have already installed.
 
For example, file Hello.mq5 delivered as part of this book is located in MQL5\Scripts\MQL5Book\p1\, where p1 means Part 1 this example relates to.
The resulting template of script Hello.mq5 contains the following text:
```
//+------------------------------------------------------------------+
//|                                                        Hello.mq5 |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
 
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
 
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
{
}
 
//+------------------------------------------------------------------+
```
It is this script that is shown in the preceding screenshots of MetaEditor and MetaTrader 5.
All strings starting with "//" are the comments and do not affect the program intent. They are neither processed by the compiler nor executed by the terminal. They are only used to exchange explanatory information among developers or to visually emphasize the code parts to enhance the text readability. For instance, in this template, the file starts with a block containing a comment where the script name and the author's copyright are expected to be specified. The second block of comments is the heading for the main function of the script — it is referred to in more detail below. Finally, the last comment string visually emphasizes the file end.
Three strings starting with a special directive, #property, provide the compiler with some attributes it builds into the program in a special manner. In our case, they are not important so far and can even be deleted. The specific directories are available to each MQL program type — we will know about them as soon as we proceed to learning the particular program types.
The main part of the script, where we are going to describe the essence of the program actions, is represented by the OnStart function. Here we have to learn the concepts of 'code block' and 'function'.
@@ -0,0 +1,73 @@
# Statements, code blocks, and functions
Thus, in the script generated by the Wizard, the OnStart function appears as follows:
```
void OnStart()
{
}
```
It is exactly our first subject matter within the context of programming in MQL5. Here again, we immediately encounter unknown concepts and character sequences. To explain them, we shall make a short digression.
A program must usually implement the following typical stages when running:
- Defining variables, i.e., named cells in the computer memory to store data;
- Organizing the source data input;
- Processing the data — an applied algorithm; and
- Organizing the output of results.
All these stages are not necessary in terms of maintaining the syntactic correctness of the program. For example, if we create a program that computes the product of "2*2", it obviously does not need any input data, because numbers necessary for multiplying are integrated in the program text. Moreover, since 2 and 2 are constant values in this expression, no named cells (variables) are required in the program. Since we know it anyway what twice two is, we don't really need to communicate the product number. Such a program would lack any real function, of course. However, it would be absolutely correct from a technical point of view.
More interestingly, the program may contain no statements on processing. Our script template specifically represents a sample 'null' program. But what is the above text fragment?
In his day, Niklaus Wirth, one of the big names in programming, gave a simple generalized definition of programming as a symbiosis of algorithms and data structures.
"Algorithm" shall mean a sequence of statements of a particular programming language. A statement is a kind of sentence, i.e., a completed utterance, articulated in a programming language according to its syntax rules. The name "statement" itself suggests that it is perceived by computers as a guide to operations. In other words, statements describe when and how the required applied data structures shall be processed. This is exactly why the interpenetration of algorithms and data structures allows putting the author's ideas into practice.
Unfortunately, in most practical tasks, the number of statements is so large that they must be systematized somehow for the human to recognize and control the program behavior.
Here too, the divide-and-conquer algorithm comes to help, which is used practically everywhere in programming and in different guises. We will learn all of them as we continue in this book, now just noting the essence.
As known, the algorithm reduces to dividing a large complex task into smaller and simpler ones. Here, we can compare this process with constructing a house or assembling a spacecraft. Both "products" consist of multiple different modules that, in turn, consist of components, and the latter ones of even smaller parts, etc.
Extending this similarity to algorithms, we can say that statements are small parts, while the entire program is a house/spacecraft. Therefore, we need structural blocks sized intermediately.
This is why it is customary, when implementing algorithms, to combine logically related statements into larger named fragments, the functions. In the required places of the program, we can address the function by its name (call it) and doing so, ask the computer to execute all statements contained inside the function. The entire program is, in fact, the largest external block and therefore, it can also be presented by the function, from which smaller functions are called or statements are executed immediately if they are not many. Now we're approaching the OnStart function.
Name OnStart is reserved in scripts to denote the ultimate function that is called by the terminal itself as a response to the user's actions when the user launches the script using the context menu command or dragging the mouse over the chart. Thus, the preceding fragment of the code defines the function OnStart that predetermines the behavior of our entire script.
Those who know programming in other languages, such as C, C++, Rust, or Kotlin, can notice the similarity of this function with the function main the core point of entering into the program.
Any script must contain the function OnStart. Otherwise, the compilation may finish with an error.
Empty function OnStart, as ours, starts being executed by the terminal (as soon as the script is launched in any manner) and immediately finishes its operation. Strictly speaking, there is no applied algorithm in our script yet, but there is already a stub function to add it.
In other types of MQL programs, there are also special functions to be defined by the programmer in their code. We will get into the specific features in the relevant parts of the book.
We will consider the function definition syntax in detail in the second part of this book. For a hands-on review of it, it is sufficient to mention the following basic essentials to understand the description of OnStart.
Since functions are usually intended for obtaining an applicable result, the characteristics of the expected value are described in a special manner in their definition: What data types should be obtained and whether the data is even necessary. Some functions can perform actions that do not require returning the value. For example, a function can be intended for changing the settings of the current chart or to send push notifications when the predefined drawdown level is reached on the account. All this can be programmed by the statements in the function, and it does not create any new data (reasonable to be returned to any other parts of the program).
In our case, the situation is similar: As the main function of the script, OnStart could return its result to the external environment only (directly into the terminal) when completed, but this would not affect the operation of the script itself in any way (because it has already finished off).
That is exactly why, before the OnStart function name, there is the word void that informs the compiler that the result is not important to us (void means emptiness). void is one of many procedure words reserved in MQL5. The compiler knows the meanings of all reserved words, and it is guided by them in reviewing the source code. Particularly, a programmer may use reserved words to define new terms for the compiler, such as the function OnStart itself.
Parentheses after the name are integral to the description of any function: They may enclose the list of function parameters. For instance, if we were writing a function taking a square of a number, we would have to provide it with one parameter for that number. Then we could call this function from any part of the program, having sent one argument over it, i.e., the specific value for the parameter. We will see later how to describe the list of parameters; it is not in this current example. This requirement is posed on the function OnStart for it is called by the terminal itself, and it never sends anything to this function as parameters.
At last, braces are used to mark the beginning and the end of the block containing statements. Immediately following the function name string, such a block will contain a set of operations performed by this function. It is also named the function body. In this case, there is nothing inside the braces. Therefore, the script template does not do anything.
The above sequence of word void, name OnStart, an empty list of parameters, and an empty code block defines the least, empty implementation of the function OnStart for the compiler. Later, adding statements into the function body, we will extend the definition of function OnStart.
Having executed the Compile command, we will make sure that the script can be successfully compiled, and that the ready program appears in the Navigator of the terminal in the folder Scripts/MQL5Book/p1. This results from the fact that, on the disk in the relevant folder, there is now the file of Hello.ex5. It can easily be checked in any file manager.
We can run the script on a chart, but the only confirmation of its execution will be the entries in the terminal log (tab Log in the Tools window; not to be mixed with the toolbar):
```
Scripts        script Hello (EURUSD,H1) loaded successfully
Scripts        script Hello (EURUSD,H1) removed
```
That is, the script is loaded, the control is sent to the function OnStart, but immediately returned to the terminal because the function does not do anything, and after that, the terminal unloaded the script from the chart.
@@ -0,0 +1,71 @@
# First program
Let's try to add to the script something simple but illustrative to demonstrate its operation. Let's rename the modified script as HelloChart.mq5.
In many programming textbooks, the initial example prints the sacramental "Hello, world". In MQL5, a similar greeting could appear as follows:
```
void OnStart()
{
  Print("Hello, world");
}
```
But we will make it more informative:
```
void OnStart()
{
  Print("Hello, ", Symbol());
}
```
Thus, we have added only one string with some language structures.
Here, Print is the name of the function embedded in the terminal and intended to display messages in the Expert Advisors log (tab Expert Advisors in the Tools window; despite its name Expert Advisors, the tab collects messages from MQL programs of all types). Unlike the function OnStart that we are defining independently, the Print function is defined for us in advance and forever. Print is one of many embedded functions constructing the MQL5 API (application programming interface).
The new line in our code denotes the statement to call the Print function sending into it the list of arguments (in parentheses) that will be printed in the log. Arguments in the list are separated by commas. In this case, there are two arguments: Line "Hello " and call for another embedded function, Symbol, that returns the name of the active instrument on the current chart (the value obtained from it will immediately get into the list of arguments of function Print, into the location from which the Symbol function has been called).
The Symbol function does not have any parameters and, therefore, nothing is sent into it inside parentheses.
For instance, if the script is located on the "EURUSD" chart, then calling the function Symbol() will return "EURUSD" and, in terms of the program being executed, the statement regarding calling the function Print will have a new look: Print("Hello, ", "EURUSD"). From a user's point of view, of course, all these calls for functions and the dynamic substitution of intermediary results are smooth and immediate. However, for a programmer, it is important to fully realize how the program is executed step by step to avoid logical errors and achieve strict compliance with the plan conceived.
The "Hello " line in double quotation marks is referred to as the literal, i.e., a fixed sequence of characters perceived by the computer as a text, as it is (as it is introduced in the source code of the program).
Thus, the printing statement above must print the two arguments one by one in the log, which should result in actually joining the two lines and obtaining "Hello, EURUSD".
Importantly, the comma inside the quotation marks will be printed in the log as a part of the line and is not processed in any special manner. Unlike that, the comma that is placed after the closing quotation mark and before calling Symbol() is the separating character in the argument list, i.e., affects the program behavior. If the first comma is omitted, the program will not lose its correctness, although it will print the word "Hello" without a comma after it. However, if the second comma is omitted, the program will stop being compiled, since the syntax of the function argument list will be broken: All values in it (in our case, these are two lines) must be separated by commas.
The compiler error will appear as follows:
```
'Symbol' - some operator expected        HelloChart.mq5        16        19
```
The compiler 'complains' of the lack of something before mentioning Symbol. This will break the compilation, and the executable file of the program is not created. Therefore, we will put the comma back in place.
This example shows us how important it is to strictly follow the syntax of the language. The same characters can work differently, being in different parts of the program. Thus, even a small omission may be critical. For instance, note the semicolon at the end of the line calling Print. The semicolon means the end of the statement here. If we forget to put it, strange compiler errors may occur.
To see this, we will try to remove this semicolon and re-compile the script. This results in obtaining new errors with the description of the problem and its place in the source code.
![Compilation errors in the MetaEditor log](pics/me_error_en.png)
Compilation errors in the MetaEditor log
```
'}' - semicolon expected        HelloChart.mq5        17        1
'}' - unexpected end of program        HelloChart.mq5        17        1
```
The first error explicitly specifies the absence of the semicolon expected by the compiler. The second error is propagated: The closing brace signaling the end of the program had been detected before the current statement ended. In the compiler's opinion, it continues, because it has not encountered the semicolon yet. It is obvious how to fix the errors: The semicolon must be placed back in the right position in the statement.
Let's compile and launch the fixed script. Although it is executed very quickly and removed from the chart practically immediately and a record confirming the script operation appears in the Experts log.
```
HelloChart (EURUSD,H1)        Hello, EURUSD
```
@@ -0,0 +1,40 @@
# Data types and values
Along with calling the embedded function Symbol, we could also use our own function that we have defined in the source code. Suppose we would like to print in the log not just "Hello", but different greetings depending on the time of day. We will determine the time of day accurate to hours: 0-8 is morning, 8-16 is afternoon, and 16-24 is evening.
It is logical to suggest that the definition structure of the new function must be similar to that of the function OnStart already familiar to us. However, its name must be unique, i.e., it should not duplicate the names of other functions or reserved words. We will study the list of these words further in this textbook, while now luckily suggesting that the word Greeting can be used as a name.
Like the Symbol function, this function must return a string; this time, however, the string must be one of the following phrases, depending on the time of day: "Good morning", "Good afternoon", or "Good evening".
Guided by common sense, we are using the common concept of string here. Apparently, it is familiar to the compiler, because we saw how it had generated a program printing the predefined text. Thus, we have smoothly approached to the concept of types in the programming language, one of the types being a string, i.e., a sequence of characters.
In MQL5, this type is described by the keyword string. This is the second type we know, the first one was void. We have already seen a value of this type, without knowing it was that: It is the literal "Hello, ". When we just insert a constant (particularly, something like a quoted text) into the source code, its type description is not required: defines the correct type automatically.
Using the OnStart function description as a sample, we can suggest how the function Greeting should appear for a first approximation.
```
string Greeting()
{
}
```
This text indicates our intention to create the Greeting function, which can return an arbitrary value of the string type. However, for the function to really return something, it is necessary to use a special statement with the return operator. It is one of many MQL5 operators: We will explore them all later. If the function has a return value type other than void, it must contain the operator return.
Particularly, to return the former greeting string "Hello, " from the function, we should write:
```
string Greeting()
{
  return "Hello, ";
}
```
Operator return stops the function execution and sends out what is to the right of it, as a result. "Out" hides the source code fragment, from which the function was called.
We have not explored all the options for writing expressions that could form an arbitrary string. However, the simplest instance with the quoted text is transferred here without any changes. It is important that the return value type coincides with the function type, as in our case. At the end of the statement, we put a semicolon.
However, we wanted to generate different greetings depending on the time of day. Therefore, the function must have an hour-defining parameter that can take values ranging from 0 through 23. Obviously, the hour number is an integer, i.e., a number that has no fractional part. It is clear that the time does not stop within an hour, and minutes are counted in it, the number of minutes being an integer, too. Then again, it is pointless to determine the time of day accurately to a minute. Therefore, we will limit ourselves to choosing the greeting by the hour number only.
For integer values, there is a special type int in MQL5. This value should be sent to the function Greeting from another place in the program, from which this function will be called. Here we have first faced the necessity of describing a named memory cell, that is, a variable.
@@ -0,0 +1,27 @@
# Variables and identifiers
A variable is a memory cell having a unique name (to be referred to without any errors), which can store the values of a certain type. This ability is ensured by the fact that the compiler allocates for the variable just enough memory that is required for it in the special internal format: Each type is sized and has a relevant memory storing format. More details on this are given in Part 2.
Basically, there is a stricter term, identifier, in the program, which term is used for the names of variables, functions, and many other entities to be learned later herein. Identifier follows some rules. In particular, it may only contain Latin characters, numbers, and underscores; and it may not start with a number. This is why the word 'Greeting' chosen for the function earlier meets these requirements.
Values of a variable can be different, and they can be changed using special statements during the program execution.
Along with its type and name, a variable is characterized by the context, i.e., an area in the program, where it is defined and can be used without any errors of compiler. Our example will probably facilitate understanding this concept without any detailed technical reasoning in the beginning.
The matter is that a particular instance of a variable is the function parameter. The parameter is intended for sending a certain value into the function. Hereof it is obvious that the code fragment, where there is such a variable, must be limited to the body of the function. In other words, the parameter can be used in all statements inside the function block, but not outside. If the programming language allowed such liberties, this would become a source of many errors due to the potential possibility to 'spoil' the function inside from a random program fragment that is not related to the function.
In any case, it is a slightly simplified definition of a variable, which is sufficient for this introductory section. We will consider some finer nuances later.
Hence, let's generalize our knowledge of variables and parameters: They must have type, name, and context. We write the first two characteristics in the code explicitly, while the last one results from the definition location.
Let's see how we can define the parameter of the hour number in the Greeting function. We already know the desired type, it's int, and we can logically choose the name: hour.
```
string Greeting(int hour)
{
  return "Hello, ";
}
```
This function will still return "Hello," whatever the hour. Now we should add some statements that would select different strings to return, based on the value of parameter hour. Please remember that there are three possible function response options: "Good morning", "Good afternoon", and "Good evening". We could suppose that we need 3 variables to describe these strings. However, it is much more convenient to use an array in such cases, which ensures a unified method of coding algorithms with access to elements.
@@ -0,0 +1,111 @@
# Assignment and initialization, expressions and arrays
An array is a named set of same-type cells that are located in memory contiguously, each being accessible by its index. In a sense, it is a composite variable characterized by a common identifier, type of values stored, and quantity of numbered elements.
For instance, an array of 5 integers can be described as follows:
```
int array[5];
```
Array size is specified in square brackets after the name. Elements are numbered from 0 through N-1, where N is the array size. They are accessed, i.e., the values are read, using a similar syntax. For example, to print the first element of the above array into the log, we could write the following statement:
```
Print(array[0]);
```
Please note that index 0 corresponds to the very first element. To print the last element, the statement would be replaced with the following:
```
Print(array[4]);
```
It is supposed, of course, that before printing an element of the array, a useful value has once been written into it. This record is made using a special statement, i.e., assignment operator. A special feature of this operator is the use of the symbol '=', to the left of which the array element (or variable) is specified, in which the record is made, while to the right of it the value to be recorded or its 'equivalent' is specified. Here, 'equivalent' hides the language ability to compute expressions of arithmetic, logic, and other types (we will learn them in Part 2). Syntax of the expressions is mostly similar to the rules of writing the equations learned in school-time arithmetic and algebra. For example, operations of addition ('+'), subtraction ('-'), multiplication ('*'), and division ('/') can be used in an expression.
Below are examples of operators to fill out some elements of the array above.
```
array[0] = 10;                       // 10
array[1] = array[0] + 1;             // 11
array[2] = array[0] * array[1] + 1;  // 111
```
These statements demonstrate various methods of assignment and constructing expressions: In the first string, literal 10 is written into element array[0], while in the second and third lines, the expressions are used, computing which leads to obtaining the results specified for visual clarity in comments.
Where array elements (or variables, in a general case) are involved in an expression, the computer reads their values from memory during program execution and performs the above operations with them.
It is necessary to distinguish the use of variables and array elements to the left of and to the right of the '=' character in the assignment statement: On the left, there is a 'receiver' of the processed data (it is always single), while on the right, there are the 'sources' of initial data for computing (there can be many 'sources' in an expression, like in the last string of this example, where the values of elements array[0] and array[1] are multiplied together).
In our examples, the '=' character was used to assign the values to the elements of a predefined array. However, it is sometimes convenient to assign initial values to variables and arrays immediately upon defining them. This is called initialization. The '=' character is used for it, too. Let's consider this syntax in the context of our applied task.
Let's describe the array of strings with the greeting options inside the function Greeting:
```
string Greeting(int hour)
{
  string messages[3] = {"Good morning", "Good afternoon", "Good evening"};
  return "Hello, ";
}
```
In the statement added, not only the messages array with 3 elements is defined, but also its initialization, i.e., filling with the desired initial values. Initialization highlights the '=' character upon variable/array name and type description. For a variable, it is necessary to specify only one value after '=' (without braces), while for an array, as we can see, we can write several values separated by commas and enclosed in braces.
Do not confuse initialization with assignment. The former is specified in defining a variable/array (and is made once), while the latter occurs in specific statements (the same variable or array element can be assigned with different values over and over again). Array elements can only be assigned separately: MQL5 does not support assigning all elements at a time, as is the case with initialization.
The messages array, being defined inside the function, is available only inside it, like the parameter hour. Then we will see how we can describe variables available throughout the program code.
How shall we transform the incoming value of hour with the hour number into one of the three elements?
Recall that, according to our idea, hour can have values from 0 through 23. If we divide it by 8 exactly, we will obtain the values from 0 through 2. For instance, dividing 1 by 8 will give us 0, and 7 by 8 will give 0 (in exact division, the fractional part is neglected). However, dividing 8 by 8 is 1, so all numbers through 15 will give us 1 when divided by 8. Numbers 16-23 will correspond with the division result of 2. Integers 0, 1, and 2 obtained shall be used as indexes to read the messages array element.
In MQL5, operation '/' allows computing the exact division for integers.
Expression to obtain the division results is similar to those we have recently considered for the array, just the parameter hour and operation '/' must be used. We will use the following statement as a demonstration of a possible implementation of the hour transformation into the element index:
```
int index = hour / 8;
```
Here, a new integer variable, index, is defined and initialized by the value of the above expression.
However, we can omit saving the intermediate value in the index variable and immediately transfer this expression (to the right of '=') inside square brackets, where the array element number is specified.
Then in the statement with operator return, we can extract the relevant greeting as follows:
```
string Greeting(int hour)
{
  string messages[3] = {"Good morning", "Good afternoon", "Good evening"};
  return messages[hour / 8];
}
```
The function is more or less ready. After a couple of sections, we will make some corrections, though. So far, let's save the project in a file under another name, GoodTime0.mq5, and try to call our function. For this reason, in OnStart, we will use the call for Greeting inside the Print call.
```
void OnStart()
{
  Print(Greeting(0), ", ", Symbol());
}
```
We have saved the separating comma (put inside lateral "Hello, ") between the greeting and the instrument name. Now there are three arguments in the Print function call: The first and the last ones will be computed on the fly using calls, respectively, of functions Greeting and Symbol, while the comma will be sent for printing as it is.
So far, we are sending the constant '0' into the function Greeting. It is its value that will get into the hour parameter. Having compiled and launched the program, we can make sure that it prints the desired text in the log.
```
GoodTime0 (EURUSD,H1)        Good morning, EURUSD
```
However, in practice, greetings must be selected dynamically, depending on the time specified by the user.
Thus, we have approached the need for arranging data input.
@@ -0,0 +1,72 @@
# Data input
The basic way of data transfer into an MQL program is to use input parameters. They are similar to those of functions and just variables, from many aspects, particularly, in terms of description syntax and principles of their further use in the code.
Moreover, an input parameter description has some essential differences:
- It is placed in the text outside of all blocks (we have learned just the blocks constituting the body of functions yet, but we will learn about the other ones later) or, in other words, beyond any pairs of braces;
- It starts with the keyword input; and
- It is initialized with a default value.
It is usually recommended to place input parameters at the start of the source code.
For instance, to define an input parameter for entering the hour number in our script, the next string should be added immediately upon the triple of directives #property:
```
input int GreetingHour = 0;
```
This record means several things.
- First, there is the GreetingHour variable in the script now, which is available from any place of the source code, including from inside of any function. This definition is called a global-level definition, which is due to the execution of item 1 from the list above.
- Second, using the input keyword makes such a variable visible inside the program and in the user interface, in the MQL5 program properties dialog, which opens when it starts. Thus, when starting the program, a user sets the necessary value of parameters (in our case, one parameter GreetingHour), and they become the values of the corresponding variables during the execution of the program.
Let's note again that the default value that we have specified in the code will be shown to the user in the dialog. However, the user will be able to change it. In this case, it is that new, manually entered value that will be included in the program (not the initialization value).
The initial value of input parameters is affected by both the initialization in the code and the user's interactive choice in launching them, and the MQL5 program type, and the way it is launched. The matter is that different types of MQL5 programs have different life cycles after being launched on charts. Thus, upon a one-time placement in the chart, indicators and Expert Advisors are 'registered' in it forever, until the user removes them explicitly. Therefore, the terminal remembers the latest settings selected and uses them automatically, for example, upon the terminal restart. However, scripts are not saved in charts between the terminal sessions. Therefore, only the default value may be shown to us when we launch the script.
Unfortunately, for some reason, the description of an input parameter does not guarantee calling the dialog of settings at the script start (for scripts as an independent MQL5 program type). For this to happen, it is necessary to add one more, script-specific directive #property into the code:
```
#property script_show_inputs
```
As we will see further, this directive is not required for other types of MQL5 programs.
We needed GreetingHour to transfer its value into the Greeting function. To do so, it is sufficient to insert it into the Greeting function call, instead of 0:
```
void OnStart()
{
  Print(Greeting(GreetingHour), ", ", Symbol());
}
```
Considering the changes we have made to describe the input parameter, let's save the new script version in file GoodTime1.mq5. If we compile and start it, we will see the data entry dialog:
![Dialog to enter the parameters of script GoodTime1.mq5](pics/goodtime1_en.png)
Dialog to enter the parameters of script GoodTime1.mq5
For instance, if we edit the value GreetingHour to 10, then the script will display the following greeting:
```
GoodTime1 (EURUSD,H1)        Good afternoon, EURUSD
```
This is a correct and expected result.
Just for the fun of it, let's run the script again and enter 100. Instead of any meaningful response, we will get:
```
GoodTime1 (EURUSD,H1)        array out of range in 'GoodTime1.mq5' (19,18)
```
We have just encountered a new phenomenon, i.e., runtime error. In this case, the terminal notifies that in position 18 of string 19, our script has tried to read the value of an array element having a non-existing index (beyond the array size).
Since errors are a permanent and necessary companion of a programmer and we have to learn how to fix them, let's talk in some more details about them.
@@ -0,0 +1,98 @@
# Error fixing and debugging
Programming art relies on the ability to instruct the program what and how it must do and also to protect it against potentially doing something wrong. The latter one is unfortunately much more difficult to execute due to multiple not very obvious factors affecting the program behavior. Incorrect data, insufficient resources, somebody else's and one's own coding errors are just to name some of the problems.
Nobody is insured against errors in coding programs. Errors may occur at different stages and are conveniently divided into:
- Compilation errors returned by the compiler when identifying a source code that does not meet the required syntax (we have already learned about such errors above); it is easiest to fix them because the compiler searches for them;
- Program runtime errors returned by the terminal, if an incorrect condition occurs in the program, such as division by zero, computing the square root of a negative, or an attempt to refer to a non-existing element of the array, as in our case; they are more difficult to detect since they usually occur not at any values of input parameters, but only in specific conditions;
- Program designing errors that lead to its complete shutdown without any tips from the terminal, such as sticking at an infinite loop; such errors may turn out to be the most complex in terms of locating and reproducing them, while the reproducibility of a problem in the program is a necessary condition for fixing it afterward; and
- Hidden errors, where the program seems to work smoothly, but the result provided is not correct; it is easy to detect if 2*2 is not 4, while it is much more difficult to notice the discrepancies.
But let's get back to the specific situation with our script. According to the error message provided to us by the MQL program runtime environment, the following statement is wrong:
```
return messages[hour / 8]
```
In computing the index of an element from the array, depending on the value of the hour variable, a value may be obtained that goes beyond the array size of three.
The debugger embedded in MetaEditor allows making sure that it really happens. All its commands are collected in the Debug menu. They provide many useful functions. Here we are going to only settle on two: Debut -> Start on Real Data (F5) and Debug -> Start on History Data (Ctrl+F5). You can read about the other ones in the MetaEditor Help.
Both commands compile the program in a special manner — with the debugging information. Such a version of the program is not optimized as in standard compilation (more details on optimization, please see Documentation), while at the same time, it allows using the debugging information to 'look inside' the program during execution: See the states of variables and function call stacks.
The difference between debugging on real data and on history data consists in starting the program on an online chart with the former one and on the tester chart in a visual mode with the latter one. To instruct the editor on what exactly chart and with which settings to use, i.e., symbol, timeframe, date range, etc., you should preliminarily open the dialog Settings -> Debug and fill out the required fields in it. Option Use specified settings must be enabled. If it is disabled, the first symbol from the Market Watch and timeframe H1 will be used in online debugging, while tester settings are used when debugging on history data.
Please note that only indicators and Expert Advisors can be debugged in the tester. Only online debugging is available to scripts.
Let's run our script using F5 and enter 100 in parameter GreetingHour to reproduce the above problem situation. The script will start executing, and the terminal will practically immediately display an error message and request for opening the debugger.
```
Critical error while running script 'GoodTime1 (EURUSD,H1)'.
Array out of range.
Continue in debugger?
```
Having responded in the affirmative, we will get into MetaEditor where the current string is highlighted in the source code, in which the error has occurred (please give a notice of the green arrow in the left field).
![MetaEditor in the debugging mode in case of an error](pics/me_debug_en.png)
MetaEditor in the debugging mode in case of an error
The current call stack is displayed in the lower left window part: All functions are listed in it (in bottom-up order), which had been called before the code execution stopped at the current string. In particular, in our script, the OnStart function was called (by the terminal itself), and the Greeting function was called from it (we called it from our code). An overview panel is in the lower right part of the window. Names of variables can be entered into it, or the entire expressions into the Expression column, and watch their values in the Values columns in the same string.
For instance, we can use the Add command of the context menu or double-click with the mouse on the first free string to enter the expression "hour / 8" and make sure that it is equal to 12.
Since debugging stopped resulting from an error, there is no sense to continue the program; therefore we can execute the Debug -> Stop command (Shift+F5).
In more complex cases of a not so obvious problem source, the debugger allows the string-by-string monitoring of the sequence of executing the statements and the contents of variables.
To solve the problem, it is necessary to ensure that, in the code, the element index always falls within the range of 0-2, i.e., complies with the array size. Strictly speaking, we should have written some additional statements checking the data entered for correctness (in our case, GreetingHour can only take a value within the range of 0-23), and then either display a tip or fix it automatically in case of violation of the conditions.
Within this introductory project, we will not go beyond a simple correction: We will improve the expression that computes the element index so that its result always falls within the required range. For this purpose, let's learn about one more operator — the modulus operator that only works for integers. To denote this operation, we use symbol '%'. The result of the modulus operation is the remainder of the integer division of dividend by the divisor. For example:
```
11 % 5 = 1
```
Here, with the integer division of 11 by 5, we would obtain 2, which corresponds with the largest factor of 5 within 11, which is 10. The remainder between 11 and 10 is exactly 1.
To fix the error in function Greeting, suffice to preliminarily perform the modulus division of hour by 24, which will ensure that the hour number will range within 0-23. Function Greeting will look as follows:
```
string Greeting(int hour)
{
  string messages[3] = {"Good morning", "Good afternoon", "Good evening"};
  return messages[hour % 24 / 8];
}
```
Although this correction will surely work well (we are going to check it in a minute), it does not concern another problem that is left beyond our focus. The matter is that the GreetingHour parameter is of the int type, i.e., it can take both positive and negative values. If we tried to enter -8, for instance, or a 'more negative' number, then we would get the same runtime error, i.e., going beyond the array; just, in this case, the index does not exceed the highest value (array size) but becomes smaller than the lowest one (particularly, -8 leads to referring to the -1st element, interestingly, the values from -7 to -1 being displayed onto the 0th element and do not cause any error).
To fix this problem, we will replace the type of parameter GreetingHour with the unsigned integer: We will use uint instead of int (we will tell about all available types in part two, and here it is uint that we need). Guided by the limit for the non-negativity of values, built in at the compiler level for uint, MQL5 will independently ensure that neither the user (in the properties dialog) nor the program (in its computation) "goes negative."
Let's save the new version of the script as GoodTime2, compile, and launch it. We enter the value of 100 for the GreetingHour parameter and make sure that, this time, the script is executed without any errors, while the greeting "Good morning" is printed in the terminal log. It is the expected (correct) behavior since we can use a calculator and check that the remainder of the modulus division of 100 by 24 gives 4, while the integer division of 4 by 8 is 0, which means morning, in our case. From the user's point of view, of course, this behavior can be considered as unexpected. However, entering 100 as the hour number was also an unexpected user action. The user probably thought that our program would go down. But this did not happen, and this is a good point. Of course, with real programs, the values entered must be validated and the user must be notified about bugs.
As an additional measure of preventing from entering a wrong number, we will also use a special MQL5 feature to give a more detailed and friendly name to the input parameter. For this purpose, we will use a comment after the input parameter description in the same string. For example, like this:
```
input uint GreetingHour = 0; // Greeting Hour (0-23)
```
Please note that we have written the words from the variable name separately in the comment (it is not an identifier in the code anymore, but a tip for the user in it). Moreover, we added the range of valid values in parentheses. When launching the script, the previous GreetingHour will appear in the dialog to enter the parameters as follows:
```
Greeting Hour (0-23)
```
Now we can be sure that, if 100 is entered as the hour, it is not our fault.
A careful reader may wonder why we have defined the Greeting function with the hour parameter and send GreetingHour into it if we could use the input parameter in it directly. Function, as a discrete logical fragment of a code, is formed for both dividing the program into visible and easy-to-understand parts and reusing them subsequently. Functions are usually called from several parts of the program or are part of a library that is connected to multiple different programs. Therefore, a properly written function must be independent of the external context and can be moved among programs.
For instance, if we need to transfer our function Greeting into another script, it will stop being compiled, since there won't be the GreetingHour parameter in it. It is not quite correct to require adding it, because the other script can compute the time in another manner. In other words, when writing a function, we should do our best to avoid unnecessary external dependencies. Instead, we should declare the function parameters that can be filled out with the calling code.
@@ -0,0 +1,33 @@
# Data output
In the case of our script, data are output by simply recording the greeting into the log using the Print function. Where necessary, MQL5 allows saving the results in files and databases, sending over the Internet, and displaying as graphical series (in indicators) or objects on charts.
The simplest way to communicate some simple momentary information to the user without making him or her looking into the log (which is a service tool for monitoring the operation of programs and may be hidden from the screen) is provided by the MQL5 API function Comment. It can be used exactly as that of Print. However, its execution results in displaying the text not in the log, but on the current chart, in its upper left corner.
For instance, having replaced Print with Comment in the text script, we will obtain such a function Greeting:
```
void OnStart()
{
  Comment(Greeting(GreetingHour), ", ", Symbol());
}
```
Having launched the changed script in the terminal, we will see the following:
![Displaying text information on the chart using the Comment function](pics/comment.png)
Displaying text information on the chart using the Comment function
If we need both display the text for the user and draw their attention to a change in the environment, related to the new information, it is better to use function Alert. It sends a notification into a separate terminal window that pops up over the main window, accompanying it with a sound alert. It is useful, for example, in case of a trade signal or non-routine events requiring the user's intervention.
The syntax of Alert is identical to that of Print and Comment.
The image below shows the result of the Alert function operation.
![Displaying a notification using the Alert function](pics/alert-ru.png)
Displaying a notification using the Alert function
Script versions with functions Comment and Alert are not attached to this book for the reader to independently try and edit GoodTime2.mq5 and reproduce the screenshots provided herein.
@@ -0,0 +1,69 @@
# Formatting, indentation, and spaces
MQL5 is among the so-called free-form languages, such as C-like and many other languages. This means that placing service symbols, such as brackets or operators, and keywords may be random, provided that syntactic rules are followed. The syntax only limits the mutual sequence of those symbols and words, while the indentation size at each string start or the number of spaces between the elements of the statement have no meaning for the compiler. In any place in the text, where a space needs to be inserted to separate language elements from each other, such as a variable type keyword and a variable identifier, a larger number of spaces can be used. Moreover, instead of spaces, it is allowed to use other symbols that denote empty space, such as tabulation and line breaks.
If there is a separating symbol (we will learn more about them in Part 2) between some elements of the statement, such as a comma ',' between function parameters, then there is no need for using any spaces at all.
Changes in formatting the source code do not modify the executable code.
Basically, there are many non-free-form languages. In some of them, forming a code block, which is performed using brace matching in MQL5, is based on equal indents from the left edge.
Due to free formatting, MQL5 allows programmers to use multiple different techniques to form the source code in order to improve its readability, visibility, and easier internal navigation.
Let's consider some examples of how the source text of the Greeting function can be recorded from our script, without changing its intent.
Here is the most 'packed' version without any excessive spaces or line breaks (a line break denoted here with the symbol '\' is only added to comply with the restrictions on publishing source codes in this book).
```
string Greeting(int hour){string messages[3]={"Good morning",\
"Good afternoon","Good evening"};return messages[hour%24/8];}
```
Here is the version, in which excessive spaces and line breaks are inserted.
```
string
Greeting ( int hour )
  {
    string messages [ 3 ]
            = {
                "Good morning" ,
                "Good afternoon" ,
                "Good evening"
              } ;
      
    return messages [ hour % 24 / 8 ] ;
  }
```
MetaEditor has a built-in code styler that allows automatically formatting the source code of the current file in compliance with one of the styles supported. A specific style can be selected in dialog Tools -> Settings -> Styler. A style is applied using Tools -> Styler command.
You should keep in mind that your spacing freedom is limited. In particular, you may not insert spaces into identifiers, keywords, or numbers. Otherwise, the compiler won't be able to recognize them. For example, if we insert just one space between digits 2 and 4 in the number 24, the compiler will return a bunch of errors trying to compile the script.
Here is a knowingly incorrectly modified string:
```
return messages[hour % 2 4 / 8];
```
Here is the error log:
```
'GoodTime2.mq5'        GoodTime2.mq5        1        1
'4' - some operator expected        GoodTime2.mq5        19        28
'[' - unbalanced left parenthesis        GoodTime2.mq5        19        18
'8' - some operator expected        GoodTime2.mq5        19        32
']' - semicolon expected        GoodTime2.mq5        19        33
']' - unexpected token        GoodTime2.mq5        19        33
5 errors, 0 warnings                6        1
```
Compiler messages may not always appear clear. It should be considered that, even upon the very first (in succession) error, there is a high probability that the internal representation of the program (as the compiler perceived it in 'mid-sentence') differs considerably from what the programmer has suggested. In particular, in this case, only the first and the second errors contain the key to understanding the problem, while all other ones are propagated.
According to the first error, the compiler expected to find the symbol of an operation between 2 and 4 (as it perceives 2 and 4 as two different numbers and not as 24 separated by a space). Alternative logic consists in the fact that a closing square bracket is omitted here, and the compiler displayed the second error: "'[' - unbalanced left parenthesis." After that running through the expression gets completely shattered, due to which the subsequent number 8 and closing bracket ']' appear inappropriate to the compiler. But in fact, if we just delete the excessive space between 2 and 4, the situation will become normal.
It is, of course, much easier to perform such an error analysis where we have intentionally added the issue. We do not always understand in practice how to remedy one situation or another. Even in the case above, supposing that you have received this broken code from another programmer and the array elements do not contain such trivial information, another correction option is easy to suspect: Either 2 or 4 must be left, because the author has probably desired to replace one number with another and not cleaned the 'footprints'.
@@ -0,0 +1,9 @@
# Mini summary
In Part 1, we got familiar with the MetaEditor framework, created a script template using MQL Master, and gradually filled the script with code to solve a simple problem. For this purpose, we used some basic principles and syntactic structures of MQL5. Then we tried the debugger in practice, fixed some issues, and came to a stable program operation.
Our script samples evolved as follows:
![In the subsequent sections of this book, we will start to explore in detail these and many other features of MQL5, the technical aspects of programming, and its applications for trading.](pics/part1gog.png)
In the subsequent sections of this book, we will start to explore in detail these and many other features of MQL5, the technical aspects of programming, and its applications for trading.
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

+23
View File
@@ -0,0 +1,23 @@
Modern trading relies heavily on computer technology. Automation now extends beyond the boundaries of exchanges and brokerage offices, becoming accessible to everyday users through specialized software solutions. Among the pioneers in this field stands MetaTrader, which emerged in the early 2000s. The latest platform version, [MetaTrader 5](https://www.metatrader5.com/en), remains at the forefront, continuously evolving with innovative features and functionalities.
A key element continuously refined within MetaTrader 5 is its built-in programming language MQL5. It enables traders to ascend to a whole new level of trading automation, commonly referred to as Algorithmic Trading. With MQL5, traders can transform their strategies into applications by writing their own indicators for analysis, scripts for executing operations, or Expert Advisor for complete trading automation. Being an automated trading system, an Expert Advisor can operate autonomously, tracking price changes and promptly alerting traders via email or SMS.
The built-in programming language allows traders to implement virtually any trading concept, from simple strategies to complex algorithms based on neural networks. MQL5 seamlessly combines the features of domain-specific and universal programming languages. Over the years, the language has acquired valuable advancements, such as support for 3D graphics, parallel computations via OpenCL, Python integration, and SQLite database support.
To unlock the full potential of MetaTrader 5, you must delve into programming. This book will help you master MQL5 and learn how to create your own trading applications.
It is assumed that the reader is already familiar with MetaTrader 5. Another prerequisite is the understanding of the fundamental principles of terminal operation within a distributed information system that facilitates trading. The terminal [Help](https://www.metatrader5.com/en/terminal/help) provides detailed information on all available features.
Furthermore, using MQL5 API, traders can access capabilities far beyond the MetaTrader 5 GUI. Master the programming language to implement complex scenarios, automating various terminal operation aspects and enhancing trading strategy efficiency.
The book is divided into 7 parts, each focusing on different aspects of MQL5 programming.
- [Part 1](/en/book/intro) introduces basic MQL5 programming principles and MetaEditor, the standard MQL5 framework. Users experienced in programming in other languages should note the features of the framework.
- [Part 2](/en/book/basis) explains the basic terms, such as types, instructions, operators, expressions, variables, code blocks, program structures. It describes how these terms are utilized in MQL5 procedural programming style. Those users who know MQL4 well can skip this part and start reading Part 3.
- [Part 3](/en/book/oop) deals with object oriented programming (OOP) in MQL5. Despite its similarity to other languages that support the OOP paradigm (especially to C++), MQL5 has certain specific features. To taste, MQL5 is sort of C±±.
- [Part 4](/en/book/common) describes common embedded functions which are applicable to in any program.
- [Part 5](/en/book/applications) covers the architectural features of MQL programs and their "majoring" in types to perform various trading tasks, such as technical analysis using indicators, chart management and marking the charts with imposing graphical objects onto them, and responses to interactive actions and events involving MQL programs.
- [Part 6](/en/book/automation) explains how to analyze trading environment and automate trading operations using robots. This part also presents the program interaction with tester in various modes, including strategy optimization.
- [Part 7](/en/book/advanced) contains information regarding the extended set of dedicated APIs facilitating the MQL5 integration with adjacent technologies, such as databases, network data exchange, OpenCL, Python, etc.
Throughout the book, the material is presented in a balanced manner, combining common approaches, examples, and technical details. The reader is guided through transitioning from one concept to another, resembling a chicken-and-egg problem inherent in learning programming. To reinforce understanding, most MQL programs discussed in the book are available as source codes for practical exploration in MetaEditor/MetaTrader 5.
@@ -0,0 +1,52 @@
# MQL5 programming fundamentals
Like any other programming language, MQL5 is based on some fundamental concepts used to create more complex structures and, eventually, programs as a whole. In this Part, we are going to learn most of the concepts, such as data types, identifiers, variables, expressions, and operators, as well as the techniques to combine various statements in the code for building the desired program operation logic.
The material assists our readers in progressing to the independent practical application of the procedural programming, This is one of the very first programming trends to solve various problems. In fact, it is the formation of a program from small steps (statements) to be executed in the required sequence for data processing. The text script shown in Part 1 of this book is an example of such a style.
This section covers a broad spectrum of fundamental concepts and tools essential for successful MQL5 programming, including the following subsections:
[Identifiers](/en/book/basis/identifiers):
- Identifiers form the foundation of any program code. This subsection discusses the purpose and rules for naming identifiers in MQL5.
[Built-in data types](/en/book/basis/builtin_types):
- MQL5 includes a variety of built-in data types, each designed to store and process specific types of information. This section provides a comprehensive understanding of basic data types.
[Variables](/en/book/basis/variables):
Variables are used to store and manage data in a program. The "Variables" section teaches the basics of working with variables and considers how to declare, initializing, and assign values to them.
[Arrays](/en/book/basis/arrays):
- Arrays provide a structured way to store data. This section covers the basics of creating and using arrays in MQL5.
[Expressions](/en/book/basis/expressions):
- Expressions form the basis of calculations and program logic. From this subsection, you will learn how to construct and evaluate expressions in MQL5.
[Type conversion](/en/book/basis/conversion):
- Data type conversion is an integral part of programming. The "Type Conversion" section provides an understanding of the process related of converting data between different types in MQL5.
[Statements:](/en/book/basis/statements)
- Statements are commands that control program execution. In this section, we will look at various types of statements and their applications.
[Functions](/en/book/basis/functions):
- Functions allow for code structuring and reuse. This section dives into the basics of creating and calling functions in MQL5.
[Preprocessor](/en/book/basis/preprocessor):
- The MQL5 preprocessor processes the source code before compilation. The "Preprocessor" section describes the principles of using preprocessor directives and their impact on the code.
Procedural programming principles will act as the basis for the subsequent learning of a more powerful paradigm, i.e., Object-Oriented Programming (OOP). It will be referred to in Part 3.
```
MQL5 Programming for Traders — Source Codes from the Book. Part 2
Examples from the book are also available in the public project \MQL5\Shared Projects\MQL5Book
```
@@ -0,0 +1,49 @@
# Identifiers
As we are going to see soon, programs are built of multiple elements that must be referred to by unique names to avoid confusion. These names are exactly what is called identifiers.
Identifier is a word composed by certain rules: Only Latin characters, underscore characters ('_'), and digits may be used in it, and the first character may not be a digit. Letters can be small (lower-case) and capital (upper-letter).
The maximum identifier length is 63 characters. The identifier may not coincide with any service words of MQL5, such as type names. You can find the full list of service words in the Help. Violating any of the identifier forming rules will cause a compilation error.
Here are some correct identifiers:
```
i             // single character
abc           // lower-case letters
ABC           // upper-case letters
Abc           // mixed-case letters
_abc          // underscore at the beginning
_a_b_c_       // underscore anywhere
step1         // digit
_1step        // underscore and digit
```
We have already seen in the script HelloChart how identifiers are used as names of variables and functions.
It is recommended to provide identifiers with meaningful names, from which the purpose or content of the relevant element becomes clear. In some cases, single-character identifiers are used, which we will discuss in the section dealing with [loops](/en/book/basis/statements/statements_for).
There are some common practices for composing identifiers. For instance, if we choose a name for a variable that stores the value of profit factor, the following options will be good:
```
ProfitFactor   // "camel" style, all words start with a capital letter
profitFactor   // "camel" style, all words but the first one start with a capital letter
profit_factor  // "snake" style, the underscore is put between all words
```
In many programming languages, different styles are used to name different entities. For example, a practice may be followed, in which variable names only start with a lower-case letter, while class names (see [Part 3](/en/book/oop)) with upper-case letters. This helps the programmer analyze the source code when working in a team or if they return to their own code fragment after a long break.
Along the above ones, there are other styles, some of which are used in special cases:
```
profitfactor   // "smooth" style, all letters are lower-case
PROFITFACTOR   // "smooth" style, all letters are upper-case
PROFIT_FACTOR  // "macro" style, all letters are upper-case with underscores between the words
```
All capitals are sometimes used in the names of [constants](/en/book/basis/variables/const_variables).
"Macro" style is conventionally used in the names of [preprocessor](/en/book/basis/preprocessor/preprocessor_define_overview) macro descriptions.
@@ -0,0 +1,55 @@
# Built-In Data Types
The data type is a fundamental concept we comfortably use in our everyday life without even thinking of its existence. It is implied based on the meaning of the information we exchange and on the processing procedures admissible for it. For example, controlling our household assets, we add and deduct numbers representing our revenues and expenses. Here, the 'number' describes a type, for which we realize fully its possible values and arithmetic operations on them. In the trading context, there is a similar value, the current account balance, in MetaTrader 5; therefore, MQL5 provides a mechanism to create and manipulate numbers.
Unlike numbers, text information, such as the name of a trading instrument, conforms to other rules. Here we can build a word of letters or a sentence of words, but it is impossible to compute the progressive total or arithmetic mean of several lines. Thus, 'line' or 'string' is another data type, not a numeric one.
Along with the purpose and a typical set of operations that are meaningful for each type, there is another important thing that differs types from each other. It's their size. For instance, the week number cannot exceed 52 within a year, while the number of seconds that have elapsed from the beginning of the year represents an astronomical shape. Therefore, to efficiently store and process such different values in the computer memory, differently sized segments can be singled out. This leads us to understand that, in fact, the generalizing concept of a 'number' may hide different types.
MQL5 allows the used of some number types differing both in the sizes of memory cells allocated for them and in some additional features. In particular, some numbers may take negative values, such as floating profit in pips, while the other ones may not, such as account numbers. Moreover, some values cannot have a fractional part and therefore, it is more cost-efficient to represent them with a stricter type of 'integers', as opposed to those of random 'numbers with a decimal point'. For instance, an account balance or the price of a trading instrument generally have values with a decimal point. At the same time, the number of orders in history or, again, the account number is always an integer.
MQL5 supports a set of universal types similar to those available in the vast majority of programming languages. The set includes integer types (different sizes), two types of real numbers (with a decimal point) of different precision, strings, and single characters, as well as the logical type that only has two possible values: true and false. Moreover, MQL5 provides its own, specific types operating with time and color.
For the sake of completeness, let's note that MQL5 allows expanding the set of types, declaring applied types in the code, i.e., structures, classes, and other entities typical of OOP; but we are going to consider them later.
Since the size of the cell where the value is stored is an important type attribute, let's touch on memory methodology.
The smallest unit of computer memory is a byte. In other words, a byte is the smallest size of a cell that a program can allocate for a separate value. A byte consists of 8 smaller 'particles', bits, each being able to be in two states: Enabled (1) or disabled (0). All modern computers use such bits at the lower level because such a binary representation of information is convenient to be embodied in hardware(in random-access memory, in processors, or while transferring the data by network cables or via WiFi).
Processing the values of different types is ensured due to the different interpretations of the bit states in memory cells. The compiler deals with this. Programmers usually do not go as low as bits; however, the language provides tools for that (see [Bitwise operations](/en/book/basis/expressions/operators_bitwise)).
There are special reserved words in MQL5 to describe data types. We have already known some of them, such as void, int, and string, from Part 1. A complete list of types is given below, each with a quick reference and size in bytes.
By their purpose, they can be conditionally divided into numeric and character-coded data (marked in the relevant columns), as well as other, specialized types, such as strings, logical (or boolean) types, date/time, and color. Type void stands apart and indicates there is no value at all. In addition to scalar types, MQL5 provides object types for operations with complex numbers, matrices, and vectors: complex, vector, and matrix. These types are used to solve various problems in linear algebra, mathematical modeling, machine learning, and other areas. We will study them in detail in Part 4 of the book.
| Type | Size
(bytes) | Number | Character | Note |
| --- | --- | --- | --- | --- |
| char | 1 | + | + | Single-byte character or a signed integer |
| uchar | 1 | + | + | Single-byte character or an unsigned integer |
| short | 2 | + | + | Two-byte character or a signed integer |
| ushort | 2 | + | + | Two-byte character or an unsigned integer |
| int | 4 | + | | Signed integer |
| uint | 4 | + | | Unsigned integer |
| long | 8 | + | | Signed integer |
| ulong | 8 | + | | Unsigned integer |
| float | 4 | + | | Signed floating-point number |
| double | 8 | + | | Signed floating-point number |
| enum | 4 | (int) | | Enumeration |
| datetime | 8 | (ulong) | | Date and time |
| color | 4 | (uint) | | Color |
| bool | 1 | (uchar) | | Logical |
| string | 10+
variable | | | String |
| void | 0 | | | Void |
| complex | 16 | + | | Structure with two double-type fields |
| vector | vector length x type size | + | | One-dimensional array of real or complex type |
| matrix | rows x columns x type size | + | | Two-dimensional array of real or complex type |
Depending on its size, different value ranges may be stored in the numeric type. Along with the above, the range may considerably vary for the integers and floating-point numbers of the same size, because different internal representations are used for them. All these cobwebs will be considered in the sections dealing with specific types.
A programmer is free to choose a numeric type based on the anticipated values, efficiency considerations, or for reasons of economy. Particularly, the smaller type size allows fitting more values of this type in memory, while integers are processed faster than floating-point numbers.
Please note that numeric and character-coded types are partly crossed. This happens because a character is stored in memory as an integer, i.e., a code in the relevant table of characters: ANSI for single-byte chars or Unicode for two-byte ones. ANSI is a standard named after an institute (American National Standards Institute), while Unicode, you guessed it, means Universal Code (Character Set). Unicode characters are used in MQL5 to make strings (type string). Single-byte characters are usually required in integrating the programs with external data sources, such as those from the Internet.
As mentioned above, numeric types can be divided into integers and floating-point numbers. Let's consider them in more detail.
@@ -0,0 +1,97 @@
# Integers
Integer types are intended for storing numbers without decimal points. They should be chosen if the applied sense of the value excludes fractions. For example, the numbers of bars on a chart or of open positions are always integers.
MQL5 allows choosing integer types sized 1-8 bytes using keywords char, short, int, and long, respectively. They all are the signed types, i.e., they can contain both positive and negative values. If necessary, integer types having the same sizes can be declared unsigned (their names starting with 'u' for 'unsigned'): uchar, ushort, uint, and ulong.
Based on the type size and being signed/unsigned, the following table shows the ranges of potential values.
| Type | min | max |
| --- | --- | --- |
| char | -128 | 127 |
| uchar | 0 | 255 |
| short | -32768 | 32767 |
| ushort | 0 | 65535 |
| int | -2147483648 | 2147483647 |
| uint | 0 | 4294967295 |
| long | -9223372036854775808 | 9223372036854775807 |
| ulong | 0 | 18446744073709551615 |
There is no need to memorize the above limiting values for each integer. There are many predefined named constants in MQL5, which can be used in a code instead of 'magic' numbers, including the lowest/highest integers. This technology is considered in a section dealing with the [preprocessor](/en/book/basis/preprocessor/preprocessor_define_simple). Here, we just list the relevant named constants: CHAR_MIN, CHAR_MAX, UCHAR_MAX, SHORT_MIN, SHORT_MAX,USHORT_MAX, INT_MIN, INT_MAX, UINT_MAX, LONG_MIN, LONG_MAX, and ULONG_MAX.
Let's explain how these values are obtained. This requires returning to bits and bytes.
The number of all possible combinations of different states of 8 bits, enabled and disabled, within one byte, is 256. This produces the range of values 0-255 that can be stored in a byte. However, interpreting them depends on the type, for which this byte is allocated. Different interpretations are ensured by the compiler, according to the programmer's statements.
The low-order (rightmost) bit in a byte means 1, the second 2, the third 4, and so on through the high-order bit that means 128. It's plain to see that these numbers are equal to two raised to a power equaling the bit number (numbering starts from 0). This is the effect of using the binary system.
| Bits | high-order | low-order |
| --- | --- | --- |
| Number | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| Value | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
Where all bits are set, this produces the sum of all powers of two, i.e., 255 is the highest value for a byte. If all bits are reset, we get zero. If a low-order bit is enabled, the number is odd.
In coding signed numbers, the high-order bit is used to mark negative values. Therefore, for a single-byte integer within the positive range, 127 becomes the highest value. For negative values, there are 128 possible combinations, i.e., the lowest value is -128. Where all bits in a byte are set, it is interpreted as -1. If the lower-order bit is reset in such a number, we will get -2, etc. If only the higher-order bit (sign) is set and all other bits are reset, we get -128.
This coding that may seem to be irrational is called "additional." It allows you to unify computations of signed and unsigned numbers at the hardware level. Moreover, it allows you not to lose one value, which would happen if the positive and negative regions were coded identically: Then we would have got two values for zero, i.e., a positive 0 and a negative 0. What is more, this would bring ambiguity.
Numbers with more bytes, i.e., 2, 4, or 8, have a similar consecutive numbering of bits and the progression of their respective values. In all cases, a criterion for the number negativity is the set high-order bit of the high-order byte.
Thus, we can use a byte to store an unsigned integer (uchar, i.e., unsigned character abbreviated) within the range of 0-255. We can also write a signed integer into the byte (for which purpose we will describe its type as char). In this case, the compiler will divide the available amount of combinations of 256 equally between positive and negative values, having displayed it onto the region from -128 through 127 (the 256th value is zero). It's plain to see that values 0-127 will be coded equally at the bit level for signed and unsigned bytes. However, large absolute values, starting from 128, will turn into negative ones (according to the scheme described in the insertion above). This "transformation" only takes place at the moment of reading or performing any operations with the value stored, with the identical internal data representation (state of bits).
We will consider this matter in more detail in the section dealing with [typecasting](/en/book/basis/conversion).
In a similar manner as with single-byte integers, it is easy to calculate that the number of bit combinations is 65536 for 2 bytes. Hence, the ranges are formed for the signed and unsigned two-byte integer, short and ushort. Other types allow storing even larger values due to increasing their byte sizes.
Please note that using an unsigned type with the same size allows doubling the highest positive value. This may be necessary for storing potentially very large quantities, for which no negative values may appear. For example, the order number in MetaTrader 5 is a value of the ulong type.
We have already encountered the integer description samples in [Part 1](/en/book/intro). In particular, input parameter GreetingHour of type uint was defined there:
```
input uint GreetingHour = 0;
```
Except for the additional keyword, input, that makes the variable visible in the list of parameters of an MQL program, other components, i.e., type, name, and optional initialization after the '=' sign, are intrinsic to all variables.
Variable description syntax will be considered in detail in the [Variables](/en/book/basis/variables) section. So far, please note the method of recording the constants of integer type. In describing a variable, constants can be specified as a default value (in the example above, it is 0). Moreover, constants can be used in [expressions](/en/book/basis/expressions), for instance, in a formula event.
It should be reminded that constants of any type, inserted in the source code, are named literals (textually: "word-for-word"). Their name derives from the fact that they are introduced into the program "as is" and used immediately at the point of description. Literals, unlike many other elements of the language, particularly variables, have no names and cannot be referred to from other points of the program.
For negative numbers, it is required to provide the minus sign '-' before the number; however, the plus sign '+' can be omitted for positive numbers, i.e., forms +100 and just 100 are identical.
It should be noted that numeric values are usually recorded in the source code within our habitual decimal notation. However, MQL5 allows using the other one, i.e., hexadecimal. It is convenient for processing bit-level information (see [Bitwise operations](/en/book/basis/expressions/operators_bitwise)).
Numbers from 0 through 9 are permitted in all digit order numbers in decimal constants, while for hexadecimal ones, along with digits, Latin symbols from A through F or from a through f (that is, case does not matter) are used additionally. "Hexadecimal digit" A corresponds with number 10 of decimal notation, B — 11, C — 12, etc., up through F equal to 15.
A distinctive feature of a hexadecimal constant is the fact that it begins with prefix 0x or 0X, followed by the significant digit orders of the number. For instance, number 1 is recorded as 0x1 in the hexadecimal system, while 16 as 0x10 (an additional higher order digit is required because 16 is greater than 15, that is, 0xF). Decimal 255 turns into 0xFF.
Let's give some more examples illustrating various situations of using integer types in describing variables (attached in script MQL5/Scripts/MQL5Book/p2/TypeInt.mq5):
```
void OnStart()
{
  int x = -10;          // ok, signed integer x = -10
  uint y = -1;          // ok, but unsigned integer y = 4294967295
  int z = 1.23;         // warning: truncation of constant value, z = 1
  short h = 0x1000;     // ok, h = 4096 in decimal
  long p = 10000000000; // ok
  int w = 10000000000;  // warning, truncation..., w = 1410065408
}
```
Variable x is initialized correctly by the permitted negative value, -10.
Variable y is unsigned. Therefore, an attempt to record a negative value in it leads to an interesting effect. Number -1 has a representation in bits, which is interpreted by the program in accordance with the unsigned type, uint. Therefore, number 4294967295 is obtained (it is actually equal to UINT_MAX).
Variable z is assigned with the floating-point number 1.23 (they will be considered in the next section), and the compiler warns about the truncation of the fractional part. As a result, integer 1 gets into the variable.
Variable h is successfully initialized by a constant in the hexadecimal form (0x1000 = 4096).
The large value 10000000000 is recorded in variables p and w, the former of which is of a long integer type (long) and processed successfully, while the latter one of the normal type (int) and, therefore, calls for the compiler warning. Since the constant exceeds the maximum value for int, compiler truncates the excessive higher order digits (bits) and, in fact, 1410065408 gets into w.
This behavior is one of the potential negative developments of type conversions that may or not may be implied by the programmer. In the latter case, it is fraught with a potential error. Clearly, in this particular example, wrong values were selected intentionally to demonstrate warnings. It is not always that obvious in a real program, which values the program is attempting to save in the integer variable. Therefore, you should look into the compiler warnings very carefully and try to make away with them, having changed the type or explicitly specified the required typecast. This will be considered in the section dealing with [Typecasting](/en/book/basis/conversion).
For integer types, arithmetic, bitwise, and other types of operations are defined (see chapter [Expressions](/en/book/basis/expressions)).
@@ -0,0 +1,97 @@
# Floating-point numbers
We use numbers with a decimal point, or real numbers, in everyday life just as often as integers. The name 'real' itself indicates that using such numbers, you can express something tangible from the real world, such as weight, length, body temperature, i.e., everything that can be measured by a non-integer amount of units, but with "a little more."
We often use real numbers in trading, too. For instance, they are used to express symbol prices or volumes in trading orders (normally permitting the fractional parts of a full-sized lot).
There are 2 real types provided in MQL5: float for normal accuracy and double for double accuracy.
In the source code, the constant values of types float and double are usually recorded as an integer and a fractional part (each being a sequence of digits), separated by the character '.', such as 1.23 or -789.01. There can be no integer or fraction (but not both at a time), but the point is mandatory. For instance, .123 means 0.123, while 123. means 123.0. Simply 123 will create a constant of integer type.
However, there is another form of recording real constants, the exponential one. In it, the integer and fractional part are followed by 'E' or 'e' (case does not matter) and an integer representing the power, to which 10 should be raised to obtain an additional factor. For instance, the following representations display the same number, 0.57, in exponential form:
```
 .0057e2
0.057e1
.057e1
57e-2
```
When recording real constants, the latter ones are defined by default as type double (they consume 8 bytes). To set type float, suffix 'F' (or 'f') should be added to the constant on the right.
Types float and double differ by their sizes, ranges of values, and number representation accuracy. All this is shown in the table below.
| Type | Size (bytes) | Minimum | Maximum | Accuracy (digit orders) |
| --- | --- | --- | --- | --- |
| float | 4 | ±1.18 * 10 -38 | ±3.4 * 10 38 | 6-9, usually 7 |
| double | 8 | ±2.23 * 10 -308 | ±1.80 * 10 308 | 15-18, usually 16 |
Range of values is shown for them in absolute terms: Minimum and maximum determine the amplitude of permitted values in both positive and negative regions. Similar to integer types, there are embedded named constants for these limiting values: FLT_MIN, FLT_MAX, DBL_MIN, DBL_MAX.
Please note that real numbers are always signed, that is, there are no unsigned analogs for them.
Accuracy shall mean the quantity of significant digits (decimal digits) the real number of the relevant type is able to store undistorted.
Indeed, the numbers of real types are not as accurate as those of integer types. This is the price to be paid for their universality and a much wider range of potential values. For instance, if an unsigned 4-byte integer (uint) has the highest value of 4294967295, i.e., about 4 million, or 4.29*109, then the 4-byte real one (float) has 3.4 * 1038, which is by 29 orders of magnitude higher. For 8-byte types, the difference is even more perceptible: ulong can house 18446744073709551615 (18.44*1018, or ~18 quintillion), while double can house 1.80 * 10308, that is, by 289 orders of magnitude more. Insertion provides more detail regarding accuracy.
Mantissa and Exponent
 
The internal representation of real numbers in memory (in the bytes allocated for them) is quite tricky. The higher-order bit is used as a marker of the negative sign (we have also seen that in integer types). All other bits are divided into two groups. The larger one contains the mantissa of the number, i.e., significant digits (we mean binary digits, i.e., bits). The smaller one stores the power (exponent), to which 10 must be raised to obtain the stored number upon multiplying it by the mantissa. Particularly, for type float mantissa is sized 24 bits (FLT_MANT_DIG), while for double it is 53 (DBL_MANT_DIG). In terms of conventional decimal places (digits), we will get the same accuracy that has been shown in the table above: 6 (FLT_DIG) is the lowest quantity of significant digits for float, while 15 (DBL_DIG) is that for double. However, depending on the particular number, it can have "lucky" combinations of bits, corresponding to a greater quantity of decimal digits. Sizes of the parameters are 8 and 11 bits for float and double, respectively.
 
Due to the exponent, real numbers get a much larger range of values. At the same time, with the increase in the exponent, the "specific weight" of the low-order digit of mantissa increases, too. This means that two neighboring real numbers that can be represented in the computer memory are substantially different. For instance, for number 1.0 the "specific weight" of the low-order bit is 1.192092896e—07 (FLT_EPSILON) in case of float and 2.2204460492503131e-016 (DBL_EPSILON) in case of double. In other words, 1.0 is indistinguishable from any number near it if such a number is below 1.192092896e—07. This may seem not very important or "not a big deal," but this uncertainty region gets larger for larger numbers. If you store in float a number about 1 billion (1*109), the last 2 digits will stop being safely stored or restored from memory (see the code sample below). However, basically, the problem is not the absolute value of a number, but the maximum quantity of digits in it, which should be recalled without losses. Equally "well," we can try to fit a number represented as 1234.56789 (which is structurally much like the price of a financial instrument) in float; and its two last digits will "float" due to the lack of accuracy in their internal representation.
 
For double, a similar situation will start showing for much greater numbers (or for a much greater quantity of significant digits), but it is still possible and often happens in practice. You should consider this when operating very large or very small real numbers and write your programs with additional checks for potential loss of accuracy. In particular, you should compare a real number with zero in a special manner. We will deal with it in the section on [comparison operators](/en/book/basis/expressions/operators_relational).
 
It may seem to a careful reader that the sizes of mantissa and exponent above are specified wrongly. Let's explain that exemplified by float. It is stored in the memory cell sized 4 bytes, that is, consumes 32 bits. At the same time, the sizes of mantissa (24) and exponent (8) sum to 32 already. Then where is the signed bit? The matter is that IT professionals arranged to store mantissa in the 'normalized' form. It will be easier to understand what it is if we consider the exponential form of recording a normal decimal number first. Let's say number 123.0 could be represented as 1.23E2, 12.3E1, or 0.123E3. A designation is considered to be the normalized form, where only one significant digit (i.e., not zero) is placed before the point. For this number, it is 1.23E2. By definition, digits from 1 through 9 are considered significant digits in decimal notation. Now we are smoothly going to the binary notation. There is only one significant digit in it, 1. It appears that the normalized form in binary notation always starts with 1, and it can be omitted (not to spend memory on it). In this manner, one bit can be saved in the mantissa. In fact, it contains 23 bits (one more higher-order unity is implicit and added automatically when reconstructing the number and retrieving it from memory). Reducing mantissa by 1 bit makes room for the signed bit.
Predominantly, where the floating-point type should be used, we choose double as a more accurate one. Type float is only used to save memory, such as when working with very large data arrays.
Some examples of using the constants of real types are shown in script MQL5/Scripts/MQL5Book/p2/TypeFloat.mq5.
```
void OnStart()
{
  double a0 = 123;      // ok, a0 = 123.0
  double a1 = 123.0;    // ok, a1 = 123.0
  double a2 = 0.123E3;  // ok, a2 = 123.0
  double a3 = 12300E-2; // ok, a3 = 123.0
  double b = -.75;      // ok, b = -0.75
  double q = LONG_MAX;  // warning: truncation, q = 9.223372036854776e+18
                        //               LONG_MAX = 9223372036854775807
  double d = 9007199254740992; // ok, maximal stable long in double
  double z = 0.12345678901234567890123456789; // ok, but truncated
                           // to 16 digits: z = 0.1234567890123457
  double y1 = 1234.56789;  // ok, y1 = 1234.56789
  double y2 = 1234.56789f; // accuracy loss, y2 = 1234.56787109375
  float m = 1000000000.0;  // ok, stored as is
  float n =  999999975.0;  // warning: truncation, n = 1000000000.0
}
```
Variables a0, a1, a2, and a3 contain the same numbers (123.0) written in different methods.
In the constant for variable b, the insignificant zero is omitted before the point. Moreover, here is the demonstration of recording a negative number using the minus sign, '-'.
An attempt is made to store the greatest integer in variable q. At this place, the compiler gives a warning, because double cannot represent LONG_MAX accurately: Instead of 9223372036854775807, there will be 9223372036854776000. It obviously demonstrates that, even though the ranges of the double values exceed those of integers vastly, it is achieved due to losing the low-order digits.
As a comparison, the maximum integer that the double type is able to store without any distortions is given as the value of variable d. In the sequence of integers, it will be followed by sporadic skips, if we use double for them.
Variable z reminds us again about the limitation on the maximum quantity of significant digits (16) a longer constant will be truncated.
Variables y1 and y2, in which the same number is recorded in different formats (double and float), allow seeing the loss of accuracy due to the transition to float.
In fact, variables m and n will be equal, because 999999975.0 is roughly stored in the internal representation and turns into 1000000000.0.
Numeric types are often used to calculate using formulas; a wide set of operations is defined for them (see [Expressions](/en/book/basis/expressions)).
Computations can sometimes lead to incorrect results, that is, they cannot be represented as a number. For example, the root of a negative number or the logarithm of zero cannot be defined. In such cases, real types can store a special value named NaN (Not A Number). In fact, there are several different types of such values that allow, for instance, telling the difference between plus infinity and minus infinity. MQL5 provides a special function, MathIsValidNumber, that checks whether the double value is a number or one of NaN values.
@@ -0,0 +1,47 @@
# Character types
Character data types are intended for storing particular characters (letters), of which strings are formed (see [Strings](/en/book/basis/builtin_types/strings)). MQL5 has 4 character types: Two sized 1 byte (char, uchar) and two sized 2 bytes (short, ushort). Types prefixed with 'u' are unsigned.
In fact, character types are integer ones, since they store an integer code of a character from the relevant table: For char, it is the table of ASCII characters (codes 0-127); for uchar, it is extended ASCII (codes 0-255); and for short/ushort, it is the Unicode table (up to 65535 characters in the unsigned version). If it is of any interest to you, ASCII is the abbreviated American Standard Code for Information Interchange.
For MQL5 strings, 2-byte chars ushort are used. 1-byte uchar types are normally used to integrate with external programs when transferring the [arrays](/en/book/basis/arrays) of random data that are packed and unpacked in other types according to applied protocols, such as for connecting to a crypto platform.
Constants of characters are recorded as letters enclosed in single quotes. However, you can also use the integer notation (see [Integers](/en/book/basis/builtin_types/integer_numbers)) considered above. At the same time, the integer must be within the range of values for 1- or 2-byte format.
Additionally, we can use the notation of escape sequences. They use a backslash ('\') as the first character followed by one of the predefined control characters and/or a numerical code. MQL5 supports the following escape sequences:
- \n — new line
- \r — carriage return
- \t — tabulation
- \\ — backslash
- \" — double quote
- \' — single quote
- \X or \x — prefix to subsequently specify a numerical code in hexadecimal format
- \0 — prefix to subsequently specify a numerical code in octal format
Basic methods of using the constants of character types are given in script MQL5/Scripts/MQL5Book/p2/TypeChar.mq5.
```
void OnStart()
{
  char a1 = 'a';  // ok, a1 = 97, English letter 'a' code
  char a2 = 97;   // ok, a2 = 'a' as well
  char b = '£';   // warning: truncation of constant value, b = -93
  uchar c = '£';  // ok, c = 163, pound symbol code
  short d = '£';  // ok
  short z = '\0';    // ok, 0
  short t = '\t';    // ok, 9
  short s1 = '\x5c'; // ok, backslash code 92
  short s2 = '\\';   // ok, backslash as is, code 92 as well
  short s3 = '\0134';// ok, backslash code in octal form
}
```
Variables a1 and a2 get the value of character 'a' (English letter) in two different ways.
There is an attempt to record '£' in variable b, but its code 163 is beyond the range char (127); therefore it is "transformed" into the signed -93 (compiler gives a warning). The variables of types uchar (c) and short (d) that follow it perceive this code as normal.
Other variables are initialized using escape sequences.
Characters can be processed with the same operations as integers (see [Expressions](/en/book/basis/expressions)).
@@ -0,0 +1,49 @@
# String type
String type is intended for storing text-based information and is marked by keyword string. String is a sequence of the ushort characters and supports the complete Unicode range, including multiple national scripts. For instance, names of financial instruments and comments in trading orders are strings.
By reason of the specific nature of strings, their size is a variable value that is equal to the doubled length of the text (quantity of characters multiplied by the "width" of a character, i.e., 2 bytes) plus one more character. This additional character is intended for the 'terminating zero' (a char coded as 0) that denotes the end of the line. Moreover, MQL5 uses some space to store service information, i.e., a reference to the place in memory where the string starts.
Unlike C++, no address of a string or any other variable can be obtained in MQL5. Direct memory access is prohibited in MQL5.
A string literal is recorded in the source code as a sequence of characters embedded in double-quotes. For example: "EURUSD" or "$". We should distinguish between strings consisting of one character, like "$", and the same single characters, like '$'. These are different data types.
An empty string appears as "". Considering the implicit terminating zero, it consumes 2 bytes, apart from service information.
Should it be necessary to use the double quote character inside the string, it must be preceded by the backslash character, transforming into a control sequence, such as "Press \"OK\"".
String initialization examples are given in script MQL5/Scripts/MQL5Book/p2/TypeString.mq5.
```
void OnStart()
{
   string h = "Hello";          // Hello
   string b = "Press \"OK\"";   // Press "OK"
   string z = "";               //
   string t = "New\nLine";      // New
                                // Line
   string n = "123";            // 123, text (not an integer value)
   string m = "very long message "
              "can be presented " 
              "by parts";
   // equivalent:
   // string m = "very long message can be presented by parts"; 
}
```
The string "Hello" is placed in variable h.
Text containing double quotes is written in variable b.
Variable z is initialized by an empty string. This is basically equivalent to describing z without initialization, but there are some finer points here. Further, as the text goes, in the section of [Initialization of variables](/en/book/basis/variables/initialization), we will get to know that uninitialized strings get a special value, NULL, unlike "", for which, as previously stated, the memory is allocated for the terminating zero. This difference affects the execution of string [comparison operators](/en/book/basis/expressions/operators_relational) and some others. As the story unfolds, we will touch upon all such aspects.
Variable t will get a text that, when printed in the log using the Print function or displayed by other methods, will be divided into 2 strings.
String "123" recorded in variable n is not a number, although it looks like that. There are some functions in MQL5 to convert text into numbers and back (see section [Data transformation](/en/book/common/conversions/conversions_numbers)). Moreover, there is a separate set of functions for [working with strings](/en/book/common/strings).
For convenience, long literals can be written in several strings, as for variable m. The general rule is as follows: All literals up to the semicolon that marks the end of the variable description are merged by the compiler. In such formatting, the key is not to forget to add an intervening space inside each fragment of the string, if necessary (for instance, to separate the words in the message as in the example above).
For strings, the summation (concatenation) operation is defined, denoted with the character '+'. We will discuss it in the chapter dealing with expressions (see [Arithmetic operations](/en/book/basis/expressions/operators_arithmetic)).
String characters can be read separately, referring to them as array elements (see [Use of arrays](/en/book/basis/arrays/arrays_usage)): If s is a string, then s[i] is the code of the ith character in it, type ushort.
@@ -0,0 +1,24 @@
# Logic (Boolean) Type
Logic type is intended for storing features that only have 2 possible states: "enabled"/"disabled". Their interface analogs are options in setup dialogs of many programs, including MetaTrader 5: Each flag may be either enabled or disabled. Checking the states of such features allows branching the logic of the program execution, thus the type name.
Logic type is defined in MQL5 under the bool keyword and consumes 1 byte of memory. For this type, two constants are reserved: true and false. Moreover, situations are permissible (and programmers often make use of it), in which bool is the result of computations with integers and real numbers, value 0 being interpreted as false, and any others as true.
Back-interpretation of the bool type value as a number is supported, as well: true is considered as 1 and false as 0.
Examples of logic type variables are given in file MQL5/Scripts/MQL5Book/p2/TypeBool.mq5.
```
void OnStart()
{
  bool t = true;          // true
  bool f = false;         // false
  bool x = 100;           // x = true
  bool y = 0;             // y = false
  int i = true;           // i = 1
  int j = false;          // j = 0
}
```
For logic type, a set of special logic operations is provided (see [Logical (Boolean) Operations](/en/book/basis/expressions/operators_logical) and [Comparison Operations](/en/book/basis/expressions/operators_relational)).
@@ -0,0 +1,43 @@
# Date and time
MQL5 provides a special type for storing time data datetime. As follows from its name, the values of datetime include both the date and time. However, where necessary, they can contain only the date or only the time of day.
Values of this type can be used in programs to monitor events, such as trading hours, news publications, or timeouts for temporarily disabling the EA trading after bad transactions.
The datetime size in memory is 8 bytes. The internal representation of data is completely identical with the ulong type, since the quantity of seconds elapsed since January 1, 1970, is stored inside. The maximum date supported is December 31, 3000.
The datetime constants are recorded as a literal string enclosed in single quotes, preceded by the character 'D'. 6 fields are allocated inside the string, with the numbers for all components of date and time in the following formats:
```
D'YYYY.MM.DD HH:mm:ss'
D'DD.MM.YYYY HH:mm:ss'
```
Here, YYYY means year, MM month, DD day, HH hours, mm minutes, and ss seconds. You can skip either date or time. It is also possible not to specify seconds or minutes with seconds.
For the maximum permitted value of date, a special constant, DATETIME_MAX, is provided in MQL5, equaling to the integer value 0x793406fff, which corresponds with  D"3000.12.31 23:59:59".
Examples of recording the values of the datetime type are shown in file MQL5/Scripts/MQL5Book/p2/TypeDateTime.mq5.
```
void OnStart()
{
  // WARNINGS: invalid date
  datetime blank = D'';           // blank = day of compilation
  datetime noday = D'15:45:00';   // noday = day of compilation + 15:45
  datetime feb30 = D'2021.02.30'; // feb30 = 2021.03.02 00:00:00
  datetime mon22 = D'2021.22.01'; // mon22 = 2022.10.01 00:00:00
  // OK
  datetime dt0 = 0;                      // 1970.01.01 00:00:00
  datetime all = D'2021.01.01 10:10:30'; // 2021.01.01 10:10:30
  datetime day = D'2025.12.12 12';       // 2025.12.12 12:00:00
}
```
The first four variables call the compiler warning about the incorrect date. In the case of blank, the literal is completely empty. In the noday variable, there is no day. In both cases, the compiler substitutes the compilation date in the constant. Variables feb30 and mon22 contain incorrect numbers of the day and month. The compiler corrects them automatically, transferring the overflow into the higher-order field (February 30 turns into March 2, while the 22nd month becomes the 10th month of the subsequent year). However, it is always recommended to get rid of warnings.
Variable dt0 demonstrates the initialization of the datetime value with an integer.
Type datetime supports the set of operations inherent in integers (see [Expressions](/en/book/basis/expressions)). This, for instance, allows adding a predefined quantity of seconds to the time (obtaining a moment in the future) or computing the difference between dates.
@@ -0,0 +1,26 @@
# Color
MQL5 has a special type for working with color. This allows the coloring of graphical objects.
To denote the type, the color keyword is used. For the color type value, 4 bytes of memory are allocated. Its internal representation is an unsigned integer containing a color in the RGB (Red, Green, Blue) format, that is, with separate intensity levels for red, green, and blue colors. Mixing these three components allows getting any visible color shade. Green and red will produce yellow, red and blue will do purple, etc.
1 byte is allocated for each component, that is, it can take values from 0 through 255. For instance, three zeros in all components produce a black color, while three maximum values of 255 are blended into white.
If we present color as uint in the hexadecimal notation, then the colors are distributed as follows: 0x00BBGGRR, where RR, GG, and BB are single-byte unsigned integers.
For its user's convenience, MQL5 supports a special form of literals to record color constants. Literal represents a triplet of numbers separated by commas and enclosed in single quotes. Character 'C' is placed before the literal. For instance, C'0,128,255' means a color with 0 for its red component, 128 for the green one, and 255 for the blue one. Hexadecimal notation of numbers can also be used: C'0x00,0x80,0xFF'.
Besides, a long list of predefined color shades is embedded in MQL5, all starting with clr. For example, clrMagenta, clrLightCyan, and clrYellow. They also include the primaries, of course: clrRed, clrGreen, and clrBlue. The full list can be found in the MetaEditor Help.
Below are some examples of setting colors (also available in file MQL5/Scripts/MQL5Book/p2/TypeColor.mq5):
```
void OnStart()
{
  color y = clrYellow;         // clrYellow
  color m = C'255,0,255';      // clrFuchsia
  color x = C'0x88,0x55,0x01'; // x = 136,85,1 (no such predefined color)
  color n = 0x808080;          // clrGray
}
```
@@ -0,0 +1,85 @@
# Enumerations
Enumerations are a group of types built in MQL5, each containing a set of named constants to describe related concepts or properties. These constants are also referred to as enumeration elements.
For example, enumeration ENUM_DAY_OF_WEEK contains constants for all days of the week:
| Identifier (ID) | Description | Value |
| --- | --- | --- |
| SUNDAY | Sunday | 0 |
| MONDAY | Monday | 1 |
| TUESDAY | Tuesday | 2 |
| WEDNESDAY | Wednesday | 3 |
| THURSDAY | Thursday | 4 |
| FRIDAY | Friday | 5 |
| SATURDAY | Saturday | 6 |
Enumeration ENUM_ORDER_TYPE describes all the order types supported in MetaTrader 5:
| Identifier (ID) | Description | Value |
| --- | --- | --- |
| ORDER_TYPE_BUY | Market buy order | 0 |
| ORDER_TYPE_SELL | Market sell order | 1 |
| ORDER_TYPE_BUY_LIMIT | Buy Limit pending order | 2 |
| ORDER_TYPE_SELL_LIMIT | Sell Limit pending order | 3 |
| ORDER_TYPE_BUY_STOP | Buy Stop pending order | 4 |
| ORDER_TYPE_SELL_STOP | Sell Stop pending order | 5 |
| ORDER_TYPE_BUY_STOP_LIMIT | Upon reaching the order price, Buy Limit pending order is placed at the StopLimit price | 6 |
| ORDER_TYPE_SELL_STOP_LIMIT | Upon reaching the order price, Sell Limit pending order is placed at the StopLimit price | 7 |
| ORDER_TYPE_CLOSE_BY | Order for closing a position by an opposite one | 8 |
There are a few dozens of various enumerations. Their names are prefixed with "ENUM_". We are going to learn them as we move through the relevant domain areas.
Each enumeration is an independent type. However, their internal representation is identical, i.e., four-byte integer (int). Each enumeration constant is coded with one number or another, but in most cases, the programmer does not need to remember these numbers, since the whole point of using enumeration is exactly to replace internal representations with evident identifiers.
The compiler ensures that the enumeration value is always one of the redefined constants. Otherwise, a warning or compilation error will occur (contextually, see the example).
This is how the ENUM_DAY_OF_WEEK enumeration appears "underneath" (script MQL5/Scripts/MQL5Book/p2/TypeEnum.mq5).
```
void OnStart()
{
  ENUM_DAY_OF_WEEK sun = SUNDAY;     // sun = 0
  ENUM_DAY_OF_WEEK mon = MONDAY;     // mon = 1
  ENUM_DAY_OF_WEEK tue = TUESDAY;    // tue = 2
  ENUM_DAY_OF_WEEK wed = WEDNESDAY;  // wed = 3
  ENUM_DAY_OF_WEEK thu = THURSDAY;   // thu = 4
  ENUM_DAY_OF_WEEK fri = FRIDAY;     // fri = 5
  ENUM_DAY_OF_WEEK sat = SATURDAY;   // sat = 6
  
  int i = 0;
  ENUM_DAY_OF_WEEK x = i; // warning: implicit enum conversion
  ENUM_DAY_OF_WEEK y = 1; // ok, equals to MONDAY
  ENUM_ORDER_TYPE buy = ORDER_TYPE_BUY;   // buy = 0
  ENUM_ORDER_TYPE sell = ORDER_TYPE_SELL; // sell = 1
  // ...
  
  // warning: implicit conversion
  //          from 'enum ENUM_DAY_OF_WEEK' to 'enum ENUM_ORDER_TYPE'
  //          'ENUM_ORDER_TYPE::ORDER_TYPE_SELL' will be used
  //          instead of 'ENUM_DAY_OF_WEEK::MONDAY'
  ENUM_ORDER_TYPE type = MONDAY;
  // compilation error: uncomment to reproduce
  // ENUM_DAY_OF_WEEK day = ORDER_TYPE_CLOSE_BY; // cannot convert enum
  // ENUM_DAY_OF_WEEK z = 10; // '10' - cannot convert enum
}
```
All constants of the days of the week are coded with numbers from 0 through 6, Sunday being the starting point. Basically, constants should not necessarily have consecutive numbers or start with 0. There are enumerations where this is not the case.
Please note that the same constants can mean different things in different enumeration types. For instance, for orders ORDER_TYPE_BUY and ORDER_TYPE_SELL in the ENUM_ORDER_TYPE enumeration, the same values (0 and 1) are used as for the days of week SUNDAY and MONDAY in ENUM_DAY_OF_WEEK.
When copying the value from a simple integer variable i into the enumeration variable x, the compiler gives a warning, since there can be a value other than the permitted constants in variable i at the program execution stage.
In variable y, we record number 1 which means MONDAY, and the compiler considers this to be a correct operation.
An attempt to write the constant of one enumeration into the variable of another enumeration (as MONDAY for variable type in the example above) may cause a warning about an implicit type conversion. This happens if the constant being written has the same value as one of the target enumeration elements. In other words, each of the two enumerations has its own element with the relevant value. Then the compiler performs an implicit conversion in the programmer's place automatically, but it uses a warning to "ask" the programmer to check whether everything is going as intended: The fact that MONDAY will be replaced with ORDER_TYPE_SELL is weird, indeed; however, we did that intentionally here for illustrative purposes.
If the element being copied does not match by its value with any element of another enumeration, a compilation error is generated, since an implicit conversion is impossible, such as when writing ORDER_TYPE_CLOSE_BY in variable day.
The commented string with variable z causes a compilation error, too, since the value 10 does not belong to ENUM_DAY_OF_WEEK. If the programmer is sure that, in an exotic case, there is still a need for recording a random value in the enumeration type variable, they can use explicit typecasting.
Explicit and implicit typecasting will be discussed in the section entitled [Typecasting](/en/book/basis/conversion).
MQL5 allows a programmer to declare their own applied enumerations using the keyword, enum. This feature is described in the next section, [Custom Enumerations](/en/book/basis/builtin_types/user_enums) (enum).
@@ -0,0 +1,102 @@
# Custom enumerations
Custom enumerations are structurally based on the int type, and the principles of using them completely coincide with what has been discussed above in the preceding section dealing with embedded enumerations. Therefore, we are describing custom enumerations here, although, strictly speaking, they are not embedded.
To describe your own enumeration in the MQL5 code, you will use the keyword enum. The simplest description form is as follows:
```
enum name
{
  element1,
  element2,
  element3
};
```
This description registers in the program an enumeration type named name with brace-enclosed comma-separated elements (their amount is only limited by the highest int value, which can be considered as no limitations in terms of practical tasks). Identifiers element1, element2, and element3 can be then used in the program within the context, in which they have been defined: Globally (i.e., outside of all functions) or inside of a function (see section [Context, visibility, and lifetime of variables](/en/book/basis/variables/scope_and_lifetime)).
Please consider the semicolon following the closing brace. It is needed since the enumeration description is a separate statement, and semicolons must be placed after any MQL5 statement.
By default, identifiers take constant values, starting with 0, each subsequent being 1 greater than the preceding one. If necessary, the programmer may define a specific value for each element, after '=' to the right of the identifier. For instance, the entry above is equivalent to this one:
```
enum name
{
  element1 = 0,
  element2 = 1,
  element3 = 2
};
```
It is permitted to specify as value only constants or expressions the compiler can compute at the compilation stage (for more details, please see the example below).
If the values are not defined for all elements, the skipped values are computed automatically based on the nearest known (preceding) ones by adding 1. For example,
```
enum name
{
  element1 = 1,
  element2,
  element3 = 10,
  element4,
  element5
};
```
Here, the first two elements take values 1 and 2 (computed), while those starting with the third one take 10 (specified explicitly), 11, and 12 (the last two ones are computed based on 10).
In script TypeUserEnum.mq5, there are some examples of describing custom enumerations.
```
const int zero = 0; // runtime value is not known at compile time
 
enum
{
  MILLION = 1000000
};
 
enum RISK
{
  // OFF      = zero, // error: constant expression required
  LOW      = -1,
  MODERATE = -2,
  HIGH     = -3,
};
 
enum INCOME
{
  LOW      = 1,
  MODERATE = 2,
  HIGH     = 3,
  ENORMOUS = MILLION,
};
 
void OnStart()
{
  enum INTERNAL
  {
    ON,
    OFF,
  };
 
  // int x = LOW; // ambiguous access, can be one of
  int x = RISK::LOW;
  int y = INCOME::LOW;
}
```
Enumeration INTERNAL shows the possibility of describing it inside of the function and, in doing so, limits the visibility/availability region of this type, which is useful in terms of name collisions.
Enumeration RISK shows that elements may be assigned with negative values. Commented element OFF cannot be described due to the attempt to initialize it with a non-constant expression: In this case, variable zero is specified there, the value of which cannot be computed by the compiler.
In enumeration INCOME, element ENORMOUS is initialized successfully by the value from the MILLION element of the other enumeration defined above. Enumerations are created at the moment of compiling and therefore, they are available in initialization expressions.
Enumeration with MILLION has no name, such enumerations are called anonymous. Their basic application is to declare constants. However, named enumerations are used more often for constants, since they allow grouping elements by their meanings.
Since there 2 enumerations defined in the example, both having elements with identical names, specifying the LOW identifier when declaring variable x leads to the "ambiguous access" compilation error, because it is not clear the element of which enumeration is meant. Please note that identifiers may have (and they do, in this case) different values.
To solve this issue, there is a special context operator: Two colons, "::". They help form the complete identifier of the language element, i.e., the enumeration element, in our case: First, the enumeration name is specified, then operator "::", and after that the element identifier. Example: RISK::LOW and INCOME::LOW. We will get to know about all operators in the relevant section.
@@ -0,0 +1,5 @@
# Void type
Type void is a special type. It means emptiness (no type) and does not consume any memory. It is only used to describe functions that do not return any values or have any parameters. We learned an example of such a function: OnStart in the HelloChart script in Part 1. This will be discussed in more detail in section [Functions](/en/book/basis/functions).
It is impossible to use type void to describe variables; however, it is the basic type in describing references to the random objects of classes. This possibility is described in [Part 3](/en/book/oop) dealing with object-oriented programming.
@@ -0,0 +1,33 @@
# Variables
In this chapter, we will learn the basic principles of working with variables in MQL5, namely those relating to embedded data types. In particular, we will consider the declaration and definition of variables, special features of initialization as the context requires, lifetime, and basic modifiers changing the properties of variables. Later on, relying on this knowledge, we will extend the abilities of variables with new custom types (unions, custom enumerations, and aliases), classes, pointers, and references.
Variables in MQL5 provide a mechanism for storing data of various types, playing an important role in organizing program logic and operations with market information. This section includes the following subsections:
[Declaration and definition of variables](/en/book/basis/variables/define_vs_declare):
- Variable declaration is the step of creating them in a program. In this section, we look at how to declare and define variables, as well as how to specify their types.
[Context, scope, and lifetime of variables](/en/book/basis/variables/scope_and_lifetime):
- Variables can exist in different contexts and scopes, which affects their availability and lifetime. This subsection covers these aspects, helping you understand how variables interact with your code.
[Initialization](/en/book/basis/variables/initialization):
- Initialization of variables involves assigning them initial values. We study methods of initialization, helping to avoid undefined program behavior.
[Static variables](/en/book/basis/variables/static_variables):
- Static variables retain their values between function calls. This section explains how to use static variables to store information between different code executions.
[Constant variables](/en/book/basis/variables/const_variables):
- Constant variables represent values that do not change during program execution. This section describes their usage and characteristics.
[Input variables](/en/book/basis/variables/input_variables):
- Input variables are used in trading robots to configure strategy parameters. We will see how to use them to create flexible and customizable trading systems.
[External variables](/en/book/basis/variables/variables_extern):
- External variables allow users to interact with the program as their values can be changed without the need to modify the code. This section explains how external variables work.
@@ -0,0 +1,34 @@
# Declaration and definition of variables
A variable is a named memory cell for storing the data of a specific type. For the program to be able to operate a variable, the programmer must declare and/or define it in the source code. In the general case, the terms declaration and definition mean different things regarding the program elements, while they practically always coincide for variables. These intricacies will be covered when we get to know about functions, classes, and special (external) variables. Here we are going to use both terms interchangeably, along with the 'description' as a generalizing one.
It would be safe to assume that a declaration contains a description of a program element with all its attributes necessary for being used in the program. Definition, however, contains the specific implementation of this element, corresponding with the declaration.
Declarations allow the compiler to interconnect all the elements of the program. Based on definitions, the compiler generates an executable code.
In the case of variables, their declaration practically always acts as their definition, since it ensures allocating memory and interpreting their contents in accordance with their types (this is exactly an implementation of a variable). The only exception is the declaration of variables with the word 'extern' (for more details, see section [External Variables](/en/book/basis/variables/variables_extern)).
Only upon the description of a variable, you can use special statements to enter values into it, read them, and refer to the variable name to move it from one part of the program into another.
In the simplest case, a statement describing a variable appears as follows:
```
type name;
```
Here, name must meet the requirements of constructing [identifiers](/en/book/basis/identifiers). As a type, you can specify any of the [embedded types](/en/book/basis/builtin_types) that we have considered in the preceding section or some other custom types we will learn a bit later how to create them. For example, integer variable i is declared as follows:
```
int i;
```
If necessary, you can describe several variables of the same type simultaneously. In this case, their names are specified in the statement, separated by commas.
```
int i, j, k;
```
An important factor is the place in the program, where the statement is located, which contains the variable description. This affects the lifetime of the variable and its accessibility from various parts of the program.
@@ -0,0 +1,106 @@
# Context, scope, and Lifetime of variables
MQL5 belongs to programming languages that use braces to group statements into code blocks.
Recall that a program consists of blocks with statements, and one block must exist definitely. In the script samples from Part 1, we saw the OnStart function. The body of this function (the brace-enclosed text following the function name) is exactly such a necessary code block.
Inside each block, the local context is formed, i.e., a region that limits the visibility and lifetime of variables described inside it. So far we have only encountered examples where braces define the body of functions. However, they can also be used to form [compound operators](/en/book/basis/statements/statements_compound), in the syntax of [the description of classes](/en/book/oop/classes_and_interfaces/classes_definition) and [namespaces](/en/book/oop/classes_and_interfaces/classes_namespace_context). All these methods also define visibility regions and will be considered in the relevant sections. At this stage, we only consider one type of local blocks, namely those inside of functions.
Along local regions, every program also has one global context, i.e., a region with the definitions of variables, functions, and other entities made beyond other blocks.
On the simple script side, in which the MQL Wizard has created the only void function OnStart, then there will only be 2 regions in it: A global one and a local one (inside the OnStart function body, although it is empty). The script below illustrates this with comments.
```
// GLOBAL SCOPE
void OnStart()
{
  // LOCAL SCOPE "OnStart"
}
// GLOBAL SCOPE
```
Please note that the global region stretches everywhere apart from function OnStart (both before and after it). Basically, it includes everything beyond any functions (if there were many), but there is nothing in this script, apart from OnStart.
We can describe variables, such as i, j, k, on the top of the file, and they will become global.
```
// GLOBAL SCOPE
int i, j, k;
void OnStart()
{
  // LOCAL SCOPE "OnStart"
}
// GLOBAL SCOPE
```
Global variables are created immediately upon starting an MQL program in the terminal and exist for the entire period of program execution.
The programmer can record and read the contents of global variables from any place in the program.
It is basically recommended to describe global variables just at the top, but it is necessary. If we move the declaration below the entire function OnStart, nothing will change basically. It will just be difficult for other programmers to immediately make sense of the code with variables, the definitions of which one has still to get to.
Interestingly, the OnStart function itself is declared in the global context, too. If we add another function, it will also be declared in the global context. Recall how we created the Greeting function in Part 1 and called it from the OnStart function. This is the effect of the function name and the method of referencing to it (how to execute it) being known throughout the source code. [Namespaces](/en/book/oop/classes_and_interfaces/classes_namespace_context) add some niceties to it; however, we will learn them later.
A local region inside each function only belongs to it: One local region is inside OnStart, and another is inside Greeting, which is its own and differs from both the local region of OnStart and the global one.
Variables described in the function body are called local. They are created according to their descriptions as of calling the relevant function during the program execution. Local variables can be only used inside the block that contains them. They are not visible or accessible from the outside. When leaving the function, local variables are destroyed.
Example of describing local variables x, y, z inside function OnStart:
```
// GLOBAL SCOPE
int i, j, k;
void OnStart()
{
  // LOCAL SCOPE "OnStart"
  int x, y, z;
}
// GLOBAL SCOPE
```
It should be noted that pairs of braces can be used in both describing the function and other statements and as themselves to form the internal code block. Unit nesting is unlimited.
Nested blocks are usually added to minimize the scope of variables used in a logically isolated small code location (if it is not set by a function for one reason or another). This allows the reduction of the probability of a false modification of the variable where it was not provided for or some undesired side effects due to the attempt to re-purpose the same variable for various needs (it is not a good practice).
Below is a sample function where unit nesting level is 2 (if we consider the block with the function body to be the first level), and 2 such blocks are created and will be executed consecutively.
```
void OnStart()
{
  // LOCAL SCOPE "OnStart"
  int x, y, z;
  
  { 
    // LOCAL SUBSCOPE 1
    int p;
    // ... use p for task 1
  }
  
  { 
    // LOCAL SUBSCOPE 2
    // y = p; // error: 'p' - undeclared identifier
    int p;    // from now 'p' is declared
    // ... use p for task 2
  }
  
  // p = x; // error: 'p' - undeclared identifier
}
```
Inside both blocks, variable p is described, which is used for various purposes in them. In fact, these are two different variables, although having the same name visible inside each block.
If the variable were taken out to the initial list of the local variables of the function, it could contain some remaining value upon exiting from the first block, thus breaking the operation of the second block. Moreover, the programmer could occasionally involve p in something else at the very beginning of the function, and then the side effects could take place in the first block.
Beyond either of the two nested blocks, variable p is unknown and therefore, an attempt to refer to it from the common block of the function leads to a compilation error ("undeclared identifier").
It should also be noted that a variable can be described not at the very beginning of the block, but in its middle or even closer to the end. Then it is defined not throughout the block, but only below its definition. Therefore, when referring to the variable above its description, the same error will occur.
Thus, the variable scope region may differ from the context (the entire block).
Both versions of the problem are illustrated in an example: Try to include any of the strings with statements p = x and y = p and compile the source code.
Memory is allocated for all the local variables of the function as soon as the control is passed inside the function. However, this is not the end of their creation. Then they are initialized (initial values are set), initialization being defined explicitly by the programmer or implicitly by the default values of the compiler. At the same time, context is of the essence, in which the variables are described.
@@ -0,0 +1,121 @@
# Initialization
In describing variables, there is a possibility to set the initial value; it is specified following the variable name and symbol '=' and must correspond with the variable type or be cast to it (typecasting can be found in the relevant [section](/en/book/basis/conversion)).
```
int i = 3, j, k = 10;
```
Here i and k are initialized explicitly, while j is not.
Both a constant (literal of the relevant type) and an expression (a kind of formula for calculations) can be specified as the initial value. We will set out [expressions](/en/book/basis/expressions) separately. In the meantime, a simple example:
```
int i = 3, j = i, k = i + j;
```
Here, variable j takes the same value as variable i, while variable k takes the sum of i and j. Strictly speaking, in all three cases, we see expressions here. However, constant (3) is a special, degenerate expression option. In the second case, the only variable name is an expression, i.e., the expression result will be the value of this variable without any transformations. In the third case, two variables, i and j, are accessed in the expression, the addition operation is executed with their values, and after that, the result gets into variable k.
Since the statement containing the description of several variables is processed from left to right, the compiler already knows the names of previous variables when analyzing yet another description.
A program usually contains many statements with variable descriptions. They are read by the compiler in a natural top-down manner. In later initializations, names can be used taken from earlier descriptions. Here are the same variables described by two separate statements.
```
int i = 3, j = i;
int k = i + j;
```
Variables without an explicit initialization also get some initial values, but they depend on the place where the variable was described, i.e., on its context.
Where there is no initialization, local variables take random values at the moment of their generation: The compiler just allocates memory for them according to the type size, while it is unknown what will be at a specific address (various computer memory areas are often re-allocated to be used in different programs after they have become unnecessary for those executed earlier).
It is usually suggested that working values will be entered in local variables without initialization somewhere later in the algorithm code, such as using [assignment operations](/en/book/basis/expressions/operator_assignment) we will talk about later on. Syntactically, it is similar to initialization, since it also uses the equal sign, '=', to transfer the value from the "structure" placed on the right of it (it can be a constant, variable, expression, or function call, into the variable on the left. Only a variable can be to the left of '='.
The programmer should ensure that reading from the uninitialized variable only takes place upon a meaningful value is assigned to it. Compiler gives a warning if this is not the case ("possible use of uninitialized variable").
Everything is different with global variables.
An example of global variables is the GreetingHour input parameter of the GoodTime2 script from Part 2. The fact that the variable was described with keyword input does not affect its other properties as a variable. We could exclude its initialization and describe it as follows:
```
input uint GreetingHour;
```
This would not change anything in the program, because global variables are implicitly initialized by the compiler using zero if there is no explicit initialization (while we also had explicit initialization with zero before).
Whatever the variable type is, implicit initialization is always performed by a value equivalent to zero. For example, for a bool variable, false will be set, while for a datetime variable there will be D'1970.01.01 00:00:00'. There is a special value, NULL, for strings. It is, if you like, an even "emptier" string than empty quotes "" because there is still some memory allocated for them, where the only terminal null character is placed.
Along with local and global variables, there is another type, i.e., static variables. The compiler initializes them with zero implicitly, too, if the programmer has not written an explicitly initial value. They will be considered in the [next section](/en/book/basis/variables/static_variables).
Let's create a new script, VariableScopes.mq5, with examples of describing local and global variables (MQL5/Scripts/MQL5Book/VariableScopes.mq5).
```
// global variables
int i, j, k;    // all are 0s
int m = 1;      // m = 1                (place breakpoint on this line)
int n = i + m;  // n = 1
void OnStart()
{
  // local variables
  int x, y, z;
  int k = m; // warning: declaration of 'k' hides global variable
  int j = j; // warning: declaration of 'j' hides global variable
  // use variables in assignment statements  
  x = n;     // ok, 1
  z = y;     // warning: possible use of uninitialized variable 'y'
  j = 10;    // change local j, global j is still 0
}
// compilation error
// int bad = x; // 'x' - undeclared identifier
```
It should be remembered that, at launching an MQL program, the terminal first initializes all global variables and then calls a function that is the starting point for the programs of a relevant type. In this case, it is OnStart for scripts.
Here, only variables i, j, k, m, n are global since they are described outside the function (in our case, we only have one function, OnStart, which is necessary for scripts). i, j, k take the value of 0 implicitly. m and n contain 1.
You can run the script in the debugging mode on a step-by-step basis and make sure that the values of variables change exactly in this manner. For this purpose, you should preliminarily set a [breakpoint](https://www.metatrader5.com/en/metaeditor/help/development/debug#breakpoint) onto the string with the initialization of one of the global variables, such as m. Put the text cursor onto this string and execute Debug -> Toggle Breakpoint (F9), and the string will be highlighted with a blue sign in the left field, which signals that the program execution will stop here if it starts working on the debugger.
Then you should actually run the program for debugging, for which purpose execute command Debug -> Start on real data (F5). At this moment, a new chart will open in the terminal, in which this script starts being executed (caption "VariableScopes (Debugging)" in the upper right corner), but it suspends immediately, and we get back to MetaEditor. We should see a picture in it as follows.
![Step-by-step debugging and viewing variables in MetaEditor](pics/me_breakpoint_en.png)
Step-by-step debugging and viewing variables in MetaEditor
A string containing a breakpoint is now marked with an arrow sign — it is the current statement the program is preparing to execute but has not executed yet. The current stack of the program is shown lower left, which consists so far of only one entry: @global_initializations. You can enter expressions lower right to monitor their real-time values. We are interested in the values of variables; therefore, let's consecutively enter i, j, k, m, n, x, y, z (each in a separate string).
You will see further that MetaEditor automatically adds variables from the current context for viewing (for instance, local variables and the function inputs, where statements are executed inside the function). But now, we are going to add x, y, and z manually and in advance, just to show that they are not defined outside the function.
Please note that, for local variables, it is written "Unknown identifier" instead of a value, because there has not been the OnStart function block yet, where they are located. Global variables i and j will first have zero values. Global variable k is not used anywhere and, therefore, it is excluded by the compiler.
If we execute one step of the program execution (execute the statement on the current code line) using commands Step Into (F11) or Step Over (F10), we will see how variable m takes value 1. Another step will continue initialization for variable n, and it will also become 1.
Here, the descriptions of global variables end and, as we know, terminal calls function OnStart upon completion of the initialization of global variables. In this case, to step into function OnStart in the stepwise mode, press F11 once again (or you can set another breakpoint in the beginning of the OnStart function).
Local variables are initialized when the execution of the program statements reaches the code block where they have been defined. Therefore, variables x, y, z are only created upon stepping into the OnStart function.
When the debugger gets inside the OnStart function, with a little luck, you will be able to see that there are really initially random values in x, y, and z. "Luck" here consists in the fact that these random values may well be zero ones. Then it will be impossible to differ them from the implicit initialization with zero, compiler performs for global variables. If the script is launched repeatedly, the "garbage" in local variables will likely be different and more illustrative. They are not initialized explicitly and, therefore, their contents may be of any kind.
In the sequence of images below, you can see the evolution of variables using the step-by-step mode of the debugger. The current string to be executed (but not executed yet) is marked with a green arrow on the fields with enumeration.
![Step-by-step debugging and viewing variables in MetaEditor (string 23)](pics/debug23_en.png)
Step-by-step debugging and viewing variables in MetaEditor (string 23)
![Step-by-step debugging and viewing variables in MetaEditor (string 24)](pics/debug24_en.png)
Step-by-step debugging and viewing variables in MetaEditor (string 24)
It is demonstrated further in the code how these variables could be used in the simplest manner in assignment operators. The value of the global variable n is copied into the local x without any problems since n has been initialized. However, in the string where the contents of variable y are copied to variable z, a warning from the compiler appears, because y is local and, as of this moment, nothing has been written in it; i.e., there is not an explicit initialization, as well as other operators that can set its value.
Inside a function, it is permitted to describe variables with the same names as already used for global variables. A similar situation may occur in nested local blocks if a variable is created in an internal block with the name existing in an external block. However, this practice is not recommended, since it may lead to logical errors. In such cases, the compiler gives a warning ("declaration hides global/local variable").
Due to such redefining, a local variable, such as k in the example above, overlaps the homonym global one inside the function. Although they have the same name, these are two different variables. Local k is known inside OnStart, while global k is known everywhere apart from OnStart. In other words, any inside-the-block operations with variable k will only affect the local variable. Therefore, upon exiting function OnStart (as if it were not the only and core function of the script), we would discover that global variable k is still equal to zero.
Local variable j does not only overlap global variable j but is also initialized by the value of the latter one. In the string containing the description of j inside OnStart, the local version of j is still being created when the initial value for it is read from the global version of j. Upon a successful definition of local j, this name overlaps the global version, and it is the local version, to which the subsequent changes in j belong.
At the end of the source code, we have commented on the attempt to declare one more global variable, bad, in the initialization of which the value of variable x is called. This string causes a compiler error since variable x is unknown beyond the OnStart function, in which it has been defined.
@@ -0,0 +1,79 @@
# Static variables
It is sometimes necessary to describe a variable inside a function, ensuring its existence for the entire duration of the program execution. For example, we want to count how many times this function has been called.
Such a variable cannot be local, because then it will lose its "long memory," since it will be created every time at calling the function and removed at exiting it. Technically, it could be described globally; however, if the variable is only used in this function, this approach is wrong in terms of program design.
First, a global variable can accidentally be changed from any place in the program.
Second, imagine what "zoo" of variables would be made in the global region of the program if we declare a global variable at the slightest pretext. Instead, it is recommended to declare variables in the smallest block (if there are several nested ones), in which they are used.
Therefore, the counter of function executions should be described inside the function. This is where the new attribute of variables helps, their static nature.
A special keyword (modifier), static, placed before the variable type in its declaration allows prolonging its lifetime up to the entire duration of program execution, that is, makes it similar to global ones. As a rule, a static variable is only defined locally, in one of the functions. Therefore, its visibility is limited by the relevant code block, as in a normal local variable.
Static variables can also be described at a global level, but do not differ from the normal global ones in any way (at least, as of writing this book). It varies from their behavior in C++: There, their visibility is limited by the file they are described in. In MQL5, a program is assembled based on one main mq5 file and, perhaps, some header files (see [directive ](/en/book/basis/preprocessor/preprocessor_include)[#include](/en/book/basis/preprocessor/preprocessor_include)); therefore, both static and normal global variables are available from all source files of the program.
A local static variable is created only once — at the moment when the program first steps into the function where this variable is described. Such a variable will only be removed at unloading the program. If a function has never been called, the local static variables described in it, if any, will never be created.
As an example, let's modify the Greeting function from Part 1 so that it gives different greetings at each call. Let's name the new script GoodTimes.mq5.
We will remove the input of the script GreetingHour and the parameter of the Greeting function. Inside the Greeting function, we will describe a new static variable, counter, of integer type, with the initial value of 0. It should be reminded that it is exactly initialization, and it will be executed only once because the variable is static.
```
string Greeting() 
{
  static int counter = 0;
  static string messages[3] =
  {
    "Good morning", "Good day", "Good evening"
  };
  return messages[counter++ % 3];
}
```
Since we know modifier static now, it is reasonable to also use it for array messages. The matter is that it was declared as local before, and it would be re-created every time at multiple calls of function Greeting (and removed at exit). This is not efficient.
It should be reminded that an array is a named set of several values of the same type, available by index specified in square brackets after the name. Much of what has been said about variables applies directly to arrays. Further nuances of working with arrays will be covered in section [Arrays](/en/book/basis/arrays).
But let's get back to our current problem. An option is chosen from the array based on the value of the counter variable in the return statement and so far appears quite cabbalistically:
```
  return messages[counter++ % 3];
```
We have already mentioned casually the modulus operation performed using character '%' in Part 1. With it, we guarantee that the element index will not be able to exceed the array size: Whatever be counter, its division modulo by 3 will either be 0 or 1, or 2.
The same applies to structure counter++, it means adding 1 to the variable value (single increment).
It is important to note that, in this notation, incrementation will take place upon having computed the entire expression, in this case, upon division counter % 3. This means that counting will start from zero, i.e., initial value. There is a possibility to make an increment before computing the expression, having written: ++counter % 3. Then counting would start from 1. We will consider the operations of this type in section [Increment and Decrement](/en/book/basis/expressions/increment_decrement).
Let's call the Greeting function from OnStart 3 consecutive times.
```
void OnStart()
{
  Print(Greeting(), ", ", Symbol());
  Print(Greeting(), ", ", Symbol());
  Print(Greeting(), ", ", Symbol());
  // Print(counter); // error: 'counter' - undeclared identifier
}
```
As a result, we will see the anticipated three strings with all greetings one after another in the log.
```
GoodTimes (EURUSD,H1)        Good morning, EURUSD
GoodTimes (EURUSD,H1)        Good afternoon, EURUSD
GoodTimes (EURUSD,H1)        Good evening, EURUSD
```
If we continue calling the function, the counter will increase, and the messages will rotate.
An attempt to refer to the counter variable at the end of OnStart (commented) will not allow the code to be compiled, since the static variable, although it continues to exist, is only available inside function Greeting.
Please note that braces are used for both forming the code blocks and initializing arrays. You should distinguish among their applications. Arrays will be considered in detail in the relevant section. However, these are not all applications of braces: Using them, we will later learn how to define custom types, structures, and classes. Static variables can also be defined inside structures and classes.
@@ -0,0 +1,30 @@
# Constant variables
However paradoxically this appears, most programming languages support the concept of constant variables. In MQL5, they are described by adding modifier const. It is placed in the variable description, preceding its type, and means that the variable value cannot be changed in any way upon its initialization by the initial value. During its entire lifetime, the variable will have the same value, i.e., a constant.
The compiler will just prevent assigning the constant with a value: The error "constant cannot be modified" will appear in the relevant string.
Modifier const is aimed at explicitly showing the programmer's intention not to change the relevant variable, if a commonly known fixed value, such as the EUR index to compute the USD index, the number of weeks in a year, etc. It is recommended to always use modifier const if you are not going to change the variable. This helps avoid potential errors later, if the programmer themselves or somebody from among their colleagues accidentally tries to write something else into the constant.
For example, we can add modifier const for the messages array in the Greeting function. This does not appear plainly useful for such a small program. However, since programs tend to grow out, any string may sooner or later "find itself" in a much more complex software environment, such as added statements, operation modes, etc. Therefore, it makes sense to have a plan B; particularly as it is so simple.
```
string Greeting() 
{
  static int counter = 0;
  static const string messages[3] =
  {
    "Good morning", "Good day", "Good evening"
  };
  // error demo: 'messages' - constant cannot be modified
  // messages[0] = "Good night";
  return messages[counter++ % 3];
}
```
In the commented string, we test recording the "Good night" string into the first element of the array (remember that numbering starts from 0). In this case, the sense of this action is just to make sure that the compiler prevents from doing that.
As is easily seen, modifiers static and const can be combined. The order of recording them is not important.
By the way, in MQL5, variables become constants in both using modifier const and declaring them with the input variables of the program.
@@ -0,0 +1,51 @@
# Input variables
When launched, all programs in MQL5 can inquire parameters from the user. The only exception is libraries that are not executed independently, but as parts of another program (see the relevant section to know more about [Libraries](/en/book/advanced/libraries)).
Input parameters of MQL programs are global variables described in the code having a special modifier of input or sinput. They become available in the dialog of program properties for the user to enter values. We saw a description of the GreetingHour input variable in the scripts of Part 1.
A special feature of input variables is the fact that their value cannot be changed in the program code, i.e., it behaves like a constant.
Input variables can only be of simple built-in types or enumerations. For enumerations, you enter the values via a drop-down list; while you use input fields in all other cases. It is not permitted to describe as input: [Arrays](/en/book/basis/arrays), [structures or unions](/en/book/oop/structs_and_unions), and [classes](/en/book/oop/classes_and_interfaces).
The developer can set the input parameter name other than the variable identifier. This name will be shown to the user in the program properties dialog. A detailed description should be added as a sing-string comment upon the definition of the input parameter.
```
input int HourStart = 0; // Start of trading (hour, including):
input int HourStop = 0;  // End of trading (hour, excluding):
```
This allows making the interface user-friendlier, detailed, and free of syntactic constraints imposed by MQL5 on [identifiers](/en/book/basis/identifiers). Moreover, names (as well as comments) can be in your native language.
For example, MetaTrader 5 comes with the source code of indicator MQL5/Indicators/Examples/Custom Moving Average.mq5 with input variables:
```
input int            InpMAPeriod = 13;        // Period
input int            InpMAShift  = 0;         // Shift
input ENUM_MA_METHOD InpMAMethod = MODE_SMMA; // Method
```
This description generates the properties dialog below.
![Sample dialog of the MQL program properties](pics/inputs-en.png)
Sample dialog of the MQL program properties
The maximum length of the text representation of an input variable as an identifier=value pair, including character "=", may not exceed 255 characters (This constraint is imposed by the internal data exchange protocols of the terminal and testing agents). This limit is especially important for string variables since the values of other types never go beyond it. As we know, the length of an identifier is limited to 63 characters; therefore, depending on the identifier length, 191-253 characters are left for the value of the input string variable. The entire text exceeding the combined threshold of 255 chars may be cropped when being transferred to the tester. If a longer string has to be entered into your MQL program, use multiple input fields (to be continued) or allow the user to specify the name of the file, from which the text should be read.
For convenience in operating MQL programs, inputs can be combined in named blocks using keyword group (semicolon in the group string end is not necessary).
```
input group "group_name"
input type identifier = value;
...
```
All variables with modifier input following the group description (up to the description of another group or to the file end) are visually displayed as a nested list under the group header in the properties dialog of the MQL program. Moreover, groups of parameters can be deployed or collapsed by a mouse click in the strategy tester applicable to both indicators and EAs.
The sinput keyword is the abbreviation of static input, both forms being equivalent.
Variables described with modifiers sinput and static input cannot be involved in optimization. It only makes sense to use them in Expert Advisors being the only MQL program type supporting optimization. For more details, see the section dealing with [Testing and optimizing Expert Advisors](/en/book/automation/tester).
@@ -0,0 +1,144 @@
# External variables
The material in this section is simultaneously complex and optional. It requires the knowledge of the concepts that are based on the analogy to C++ and those considered hereinbelow. At the same time, the effect of the language structure described can be achieved in another manner, while its flexibility is a potential source of errors.
MQL5 allows describing variables as external ones. This is made using the extern keyword and is only permitted in the [global context](/en/book/basis/variables/scope_and_lifetime).
For an external variable, syntax basically repeats a normal description but it additionally has the 'extern' keyword while initialization is prohibited:
```
extern type identifier;
```
Describing a variable as external means that its description is delayed and must occur later in the source code, usually in another file (connecting files using the [#include](/en/book/basis/preprocessor/preprocessor_include)[ directive](/en/book/basis/preprocessor/preprocessor_include) will be considered in the chapter dealing with the [preprocessor](/en/book/basis/preprocessor)). Several different source files can have a description of the same external variable, that is, those having identical types and identifiers. All such descriptions refer to the same variable.
It is assumed that this variable will be completely described in one of the files. If the variable is not defined anywhere in the code without the extern keyword, the "unresolved extern variable" compilation error is returned (similar to a linker error in C++ in such cases).
Describing an external variable allows using it efficiently in the source code of a particular file. In other words, it enables compiling a given module, although the variable is not created in this module.
Using extern in MQL5 is not so insistent as in C++ and in most cases, may be replaced by enabling a header file with general descriptions of the variables to be declared as extern. It is sufficient to perform these definitions conventionally. The compiler ensures adding each attached file to the source code only once. Considering that in MQL5 a program always consists of one compilable unit mq5, there is no C++ problem here, with the potential error of the multiple definitions of the same variable due to enabling the header in different units.
Even an additional mq5 (not mqh) file is attached in the #include directive, it does not equally compete with the main unit, for which compilation is launched; instead, it is considered as one of the headers.
Unlike C++, MQL5 does not allow specifying an initial value for an external variable (initialization in C++ leads to ignoring the word extern). If you try to set an initial value, you will get a compilation error "extern variable initialization is not allowed".
Generally, describing a variable as external can be considered a kind of "soft" description: It ensures the appearance of the variable and excludes the overriding error that would occur if the variable is described in several files without the extern modifier.
However, this can be a source of errors. If in different header files, by coincidence, identical variables are described for different purposes, then no keyword extern allows identifying a collision, while with extern, the variables will become one, and the program operation logic will most likely be broken.
As external, both variables and functions can be described (they will be considered [below](/en/book/basis/functions)). For functions, describing them with the attribute as external is a rudiment (i.e., it is compiled, but does not make any changes). The following two declarations of a function are equivalent:
```
extern return_type name([parameters]);
      return_type name([parameters]);
```
In this sense, the presence/absence of extern can only be used to stylistically distinguish between a forward description of a function from the current unit (no extern) or from an external one (extern is present).
You can use extern in both the mq5 unit to be compiled and header files to be attached.
Let's consider some options for using extern: They are entered in different files, i.e., main script ExternMain.mq5 and 3 attachable files: ExternHeader1.mqh, ExternHeader2.mqh, and ExternCommon.mqh.
In the main file, only ExternHeader1.mqh and ExternHeader2.mqh are attached, while we will need ExternCommon.mqh a bit later.
```
// source code from mqh files will be substituted implicitly
// in the main mq5 file, instead of these directives
#include "ExternHeader1.mqh"
#include "ExternHeader2.mqh"
```
In header files, two conditionally useful functions are defined: In the first one, function inc for the x variable increment, while in the second, function dec for the x variable decrement. It is variable x that is described in both files as external:
```
// ExternHeader1.mqh
extern int x;
void inc()
{
   x++;
}
// -----------------
// ExternHeader2.mqh
extern int x;
void dec()
{
   x--;
}
```
Due to this description, each of the mqh files is compiled in a regular way. When they are included in an mq5 file together, the entire program is compiled, too.
If the variable were defined in each file without the word extern, the re-defining error would occur in compiling the program as a whole. If we had transferred the definition of x from header files into the main unit, header files would have stopped being compiled (it is not a problem for somebody, perhaps; however, in larger programs, developers like checking the compilation ability of immediate corrections without compiling the entire project).
In the main script, we define a variable (in this case, with an initial value of 2, while if we do not specify the value, the default 0 will be used) and call the conditionally useful functions, as well as print the x value.
```
int x = 2;
   
void OnStart()
{
   inc();  // uses x
   dec();  // uses x
   Print(x); // 2
   ...
}
```
In file ExternHeader1.mqh, there is the description of variable short z (without extern). A similar description is commented upon in the main script. If we make this string active, we will get the error mentioned before ("variable already defined"). This is done to illustrate the potential problem.
In ExternHeader1.mqh, extern long y is described, too. At the same time, in file ExternHeader2.mqh, the homonym external variable has another type: extern short y. If the latter description were not "moved" into a comment preemptively, the types incompatibility error ("variable 'y' already defined with different type") would occur here. Summary: Either types must coincide or variables must not be external. If both options are not good, it means that there is a mistype in the name of one of the variables.
Moreover, it should be noted that variable y is not explicitly initialized. However, the main script calls it successfully and prints 0 in the log:
```
long y;
   
void OnStart()
{
   ...
   Print(y); // 0
}
```
Finally, there is a possibility provided in the script to try an alternative of the external twin variables, exemplified by the already known variable x. Instead of describing extern int x, each of the files ExternHeader1.mqh and ExternHeader2.mqh can include another common header, ExternCommon.mqh, in which there is the description of int x (without extern). It becomes the only description of x in the project.
This alternative mode of assembling the program is enabled when activating [macro](/en/book/basis/preprocessor/preprocessor_define_simple) USE_INCLUDE_WORKAROUND: It is in the comment at the beginning of the script:
```
#define USE_INCLUDE_WORKAROUND // this string was in the comment
#include "ExternHeader1.mqh"
#include "ExternHeader2.mqh"
```
In this configuration, particular include files will still be compilable, as well as the entire project. In a real project, without using this method, the common mqh file would be included in ExternHeader1.mqh and ExternHeader2.mqh unconditionally (no USE_INCLUDE_WORKAROUND conditions). In this example, switching between the two threads of instructions is based on USE_INCLUDE_WORKAROUND is only needed to demonstrate both modes. For example, the simplified version of ExternHeader2.mqh should appear as follows:
```
// ExternHeader2.mqh
#include "ExternCommon.mqh" // int x; now here
 
void dec()
{
   x--;
}
```
We can check in the MetaEditor log that file ExternCommon.mqh loaded only once, although it is referenced in both ExternHeader1.mqh and ExternHeader2.mqh.
```
'ExternMain.mq5'
'ExternHeader1.mqh'
'ExternCommon.mqh'
'ExternHeader2.mqh'
code generated
```
If the x variable is "registered" in ExternCommon.mqh, we shall not re-define it (without extern) in the main unit since this would cause a compilation error, but we can simply assign to it the desired value at the beginning of the algorithm.
@@ -0,0 +1,7 @@
# Arrays
An array is a tool for cluster-based storing and processing the data of random types. They are supported practically in any programming language. They are especially important in MQL5 because they represent a convenient method of arranging serial data relevant to trading tasks. Quotes, readings of indicators, account trading history with orders and transactions, and news are all examples of serial data, that is, the sequences of time-varying values.
The array can be considered a container variable: It can contain a predefined quantity of values of the same type, which are identified by both their name and index (position number).
In this section, we are going to consider the common syntax of describing arrays and calling them, exemplified by [embedded data types](/en/book/basis/builtin_types). In the subsequent parts of this book, with acquiring information on how to extend the system of types due to the object-oriented technology, we will use arrays in conjugation with them to get new opportunities.
@@ -0,0 +1,36 @@
# Array characteristics
Before giving an account of the syntactic particulars of declaring arrays in MQL5 and practices of working with them, let's consider some basic concepts of constructing the arrays.
The core characteristic of an array is the number of dimensions. In a one-dimension array, its elements are placed one by one, like a row of soldiers, and just one number (index) is sufficient to refer to them. Bar-by-bar prices of opening a financial instrument to the given history depth can be saved in such an array.
In a two-dimensional array, its elements diverge in two logically perpendicular directions, forming a kind of a square (or rectangular, in a general case), two indices being required for each element, i.e., one in each dimension. Such an array could be used to store price quads (Open, High, Low, and Close) for each history bar. Bar numbers would be counted with the first dimension, while the second one is used for numbers from 0 through 3, denoting one of the price types.
A three-dimensional array is the equivalent of a cube (or, more strictly in terms of geometry, right-angled parallelepiped) with three axes. Continuing the example with the array of bar-by-bar prices, we could add to it the third dimension responsible for iterating financial instruments from the Market Watch.
For each dimension, the array has a certain length (size) setting the range of possible indexes. If history is supposed to be loaded for 1,000 bars and 10 instruments, we would get an array sized 1,000 elements in the first dimension, 4 elements in the second one (OHLC), and 10 in the third one.
The product of sizes in all dimensions provides the total number of the array elements; in our case, it is 40,000. In MQL5, it may not exceed 2147483647 (maximum for int).
It is already difficult to imagine a solid shape for a 4-dimensional array because we live in a 3D world. However, MQL5 permits the creation of arrays having up to four dimensions.
It should be noted that you can always use a one-dimensional array instead of a multidimensional one with a random number of dimensions, including more than 4. This is just a matter of arranging the recomputing of several indexes into a continuous one. For example, if a two-dimensional array has 10 columns (dimension 1, axis X) and 5 rows (dimension 2, axis Y), it can be transformed into a one-dimensional array with the same quantity of elements, i.e., 50. In this case, the element index will be obtained by the following formula:
```
index = Y * N + X
```
Here, N is the number of elements in the first dimension, in our case, 10; it is the size of each row; Y is the row number (0..4); and X is the column number (0..9) in the row.
Sizes across dimensions are another characteristic that separates an array from a variable. Thus, the number of dimensions and size in each dimension must be specified in some manner in the description, along with the array name and data type (see [the following section](/en/book/basis/arrays/arrays_declaration)).
You should distinguish between the size of a variable (array element) in bytes and that of an array as the number of elements in it. Theoretically, the full array size in terms of memory it consumes must be the product of the size of one element (depending on the data type) and the number of elements. However, this formula does not always work in practice. Particularly, since strings may have different lengths, it is quite difficult to evaluate the memory volume consumed by a string array.
According to the memory allocation method, arrays can be dynamic or fixed-size.
A fixed-size array is described in the code with exact sizes in all dimensions. It is impossible to resize it later. However, practical tasks often occur, in which the amount of data to be processed is contingent and therefore, it is desirable to resize the array during the algorithm operation. Dynamic arrays exist for this particular purpose. As we will see further, they are described without specifying the first-dimension size and can then be "stretched" or "compacted" using the special MQL5 API functions.
MQL5 Documentation uses ambiguous terminology that names fixed-size array static. This concept is also used for the 'static' modifier that can be applied to the array. If such an array is declared dynamic, then it is simultaneously non-static in terms of memory allocation and static in terms of the 'static' modifier. To exclude ambiguousness, the static character in this book will only mean the declaration attribute.
Along with dynamic and fixed-size arrays, there are special arrays in MQL5 to store quotes and the buffers of technical indicators. Such arrays are named timeseries arrays since their indexes correspond with timing. In fact, these arrays are one-dimensional and dynamic. However, unlike other dynamic arrays, the terminal itself allocates memory for them. We will consider them in the sections dealing with [timeseries](/en/book/applications/timeseries) and [indicators](/en/book/applications/indicators_make).
@@ -0,0 +1,127 @@
# Description of arrays
Array description inherits some features of variable descriptions. To start with, we should note that arrays may be global and local, based on the place of their declaration. Similarly to variables, modifiers const and static can also be used in describing an array. For a one-dimension fixed-size array, the declaration syntax appears as follows:
```
type static1D[size];
```
Here, type and static1D denote the type name of elements and the array identifier, respectively, while size in square brackets is a size-defining integer constant.
For multidimensional arrays, several sizes must be specified, according to the quantity of dimensions:
```
type static2D[size1][size2];
type static3D[size1][size2][size3];
type static4D[size1][size2][size3][size4];
```
Dynamic arrays are described in a similar manner, except that a skip is made in the first square brackets (before using such an array, the required memory volume must be allocated for it using the ArrayResize function, see the section dealing with [dynamic arrays](/en/book/common/arrays/arrays_dynamic)).
```
type dynamic1D[];
type dynamic2D[][size2];
type dynamic3D[][size2][size3];
type dynamic4D[][size2][size3][size4];
```
For fixed-size arrays, initialization is permitted: Initial values are specified for the elements after the equal sign, as a comma-separated list, the entire list being enclosed in braces. For example:
```
int array1D[3] = {10, 20, 30};
```
Here, a 3-sized integer array takes the values of 10, 20, and 30.
With an initialization list, there is no need to specify the array size in square brackets (for the first dimension). The compiler will assess the size automatically by the list length. For example:
```
int array1D[] = {10, 20, 30};
```
Initial values can be both constants and the constant expressions, i.e., formulas the compiler can compute during compilation. For example, the following array is filled with the number of seconds in a minute, hour, day, and week (representation as formulas is more illustrative than 86400 or 604800):
```
int seconds[] = {60, 60 * 60, 60 * 60 * 24, 60 * 60 * 24 * 7};
```
Such values are usually designed as a preprocessor macro in the code beginning, and then the name of this macro is inserted everywhere where it is necessary in the text. This option is described in the section related to the [Preprocessor](/en/book/basis/preprocessor/preprocessor_define_overview).
The number of initializing elements may not exceed the array size. Otherwise, the compiler will give the error message, "too many initializers". If the quantity of values is smaller than the array size, the resting elements are initialized by zero. Therefore, there is a brief notation to initialize the entire array by zeros:
```
int array2D[2][3] = {0};
```
Or just empty braces:
```
int array2D[2][3] = {};
```
It works regardless of the number of dimensions.
To initialize multidimensional arrays, the lists must be nested. For example:
```
int array2D[3][2] = {{1, 2}, {3, 4}, {5, 6}};
```
Here, the first-dimension size of the array is 3; therefore, two commas frame 3 elements inside the external braces. However, since the array is two-dimensional, each of its elements is an array, in turn, the size of each being 2. This is why each element represents a list in braces, each list containing 2 values.
Supposing, we need a transposed array (the first size is 2, and the second one is 3), then its initialization will change:
```
int array2D[2][3] = {{1, 3, 5}, {2, 4, 6}};
```
We can skip one or more values in the initialization list, if necessary, having marked their places with commas. All skipped elements will also be initialized by zero.
```
int array1D[3] = {, , 30};
```
Here, the first elements will be equal to 0.
The language syntax permits placing a comma after the last element:
```
string messages[] =
{
  "undefined",
  "success",
  "error",
};
```
This simplifies adding new elements, especially for multi-string entries. Particularly, if we forget to enter a comma before the newly added element in a string array, the old and the new strings will turn out to be fused within one element (with the same index), while no new element will appear. Moreover, some arrays may be generated automatically (by another program or by macros). Therefore, the unified appearance of all elements is natural.
"Heap" and "Stack"
 
With arrays that can potentially be large, it is important to make the distinction between global and local location in memory.
 
Memory for global variables and arrays is distributed within the 'heap', i.e., free memory available to the program. This memory is not practically limited by anything, apart from the physical characteristics of your computer and operating system. The name of 'heap' is explained by the fact that differently sized memory areas are always either allocated or deallocated by the program, which results in the free areas being randomly scattered within the entire bulk.
 
Local variables and arrays are located in the stack, i.e., a limited memory area preliminarily allocated for the program, especially for local elements. The name of 'stack' derives from the fact that, during the algorithm execution, the nested calls of functions take place, which accumulate their internal data according to the "piled-up" principle: For instance, OnStart is called by the terminal, a function from your applied code is called from OnStart, then your other function is called from the previous one, etc. At the same time, when entering each function, its local variables are created that continue being there when the nested function is called. It creates local variables, too, which get onto the stack somewhat over the preceding ones. As a result, a stack usually contains some layers of the local data from all functions that had been activated on the path to the current code string. Not until the function being on the top of the stack is completed, its local data will be removed from there. Generally, the stack is a storage that works according to the FILO/LIFO (First In Last Out, Last In First Out) principle.
 
Since the stack size is limited, it is recommended to create only local variables in it. However, arrays can be quite large to exhaust the entire stack very soon. At the same time, the program execution is completed with an error. Therefore, we should describe arrays at a global level as static (static) or allocate memory for them dynamically (this is also done from the heap).
@@ -0,0 +1,112 @@
# Using arrays
Values are written to and read from the array elements using a similar syntax and specifying the required indices in square brackets. To put a value into an element, we will use the [assignment operation](/en/book/basis/expressions/operator_assignment) '='. For example, to replace the value of the 0th element of a one-dimensional array:
```
array1D[0] = 11;
```
Indexing starts with 0. The index of the last element is equal to the quantity of elements minus 1. Of course, we can use as an index both a constant and any other expression that can be reduced to the integer type (for more details on expressions, see the [following chapter](/en/book/basis/expressions)), such as an integer variable, a function call, or an element of another array with integers (the indirect addressing).
```
int index;
// ... 
// index = ... // assign an index somehow
// ...
array1D[index] = 11;
```
For multidimensional arrays, indexes must be specified for all dimensions.
```
array2D[index1][index2] = 12;
```
Permitted integer types exclude long and ulong for indices. If we try to use the value of a "long integer" as an index, it will be implicitly converted into int, wherefore the compiler gives the warning "possible loss of data due to type conversion."
Reading access to the array elements is arranged according to the same principle. For example, this is how an array element can be printed in the log:
```
Print(array2D[1][2]);
```
In script GoodTimes, we have already seen the description of the local static array messages with the strings of greetings (inside the Greeting function) and the use of its elements in the return operator.
```
string Greeting() 
{
  static int counter = 0;
  static const string messages[3] = // description
  {
    "Good morning", "Good day", "Good evening" // initialization
  };
  return messages[counter++ % 3];   // using
}
```
When executing return, we read the element that has the index defined by the expression: counter++ % 3. Division modulo 3 (denoted as '%') ensures that counter increased every time increased by 1 will be forced to the range of the correct values of indices: 0, 1, or 2. If there were not modulo divisions, the index of the requested element would exceed the array size, starting from the 4th call of this function. In such cases, the program execution time error occurs ("array out of range"), and it is unloaded from the chart.
MQL5 API includes universal functions for many operations with arrays: Allocating memory (for dynamic arrays), filling, copying, sorting, and searching in arrays are all considered in the section [Working with Arrays](/en/book/common/arrays). However, we are presenting one of them now: [ArrayPrint](/en/book/common/arrays/arrays_print) allows the printing of the array elements in the log in a convenient format (considering dimensions).
Script Arrays.mq5 demonstrates some examples of describing arrays, and the results are printed in the log. We will consider manipulations with the elements of arrays later, upon having studied loops and expressions.
```
void OnStart()
{
  char array[100];      // without initialization
  int array2D[3][2] =
  {
    {1, 2},             // illustrative formatting
    {3, 4},
    {5, 6}
  };
  int array2Dt[2][3] =
  {
    {1, 3, 5},
    {2, 4, 6}
  };
  ENUM_APPLIED_PRICE prices[] =
  {
    PRICE_OPEN, PRICE_HIGH, PRICE_LOW, PRICE_CLOSE
  };
  // double d[5] = {1, 2, 3, 4, 5, 6}; // error: too many initializers
  ArrayPrint(array);    // printing random "garbage" values
  ArrayPrint(array2D);  // showing the 2D array in the log
  ArrayPrint(array2Dt); // a "transposed" appearance of the same data 2D
  ArrayPrint(prices);   // getting to know the values of the price enumeration elements
}
```
One of the log entry options is represented below.
```
[ 0]   0   0   0   0   0   0   0   0   0   0   0   0 -87 105  82 119   0
       0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
[34]   0   0   0 -32  -3  -1  -1   7   0   0   2   0   0   0   0   0   0
       0   2   0   0   0   0   0   0   0 -96 104  82 119   0   0   0   0
[68]   0   0   3   0   0   0   0   0  -1  -1  -1  -1   0   0   0   0 100
      48   0   0   0   0   0   0   0   0   0   0   0   0   0   0
    [,0][,1]
[0,]   1   2
[1,]   3   4
[2,]   5   6
    [,0][,1][,2]
[0,]   1   3   5
[1,]   2   4   6
2 3 4 1
```
The array named array does not have any initialization and therefore, memory allocated for it may contain random values. Values will change at each script run. It is recommended to always initialize local arrays, just in case.
Arrays array2D and array2Dt are printed in the log in an illustrative form, as matrices. It is in no way linked to the fact that we have formatted the initialization lists in the source code in the same manner.
The prices array has the type of the embedded enumeration ENUM_APPLIED_PRICE. Basically, arrays can be of any type, including structures, function pointers, and other things that we are going to consider. Since enumerations are based on the int type, the values are displayed by digits, not by the names of elements (to obtain the name of a specific element of the enumeration, there is the function [EnumToString](/en/book/common/conversions/conversions_enums), but its mode is not supported in [ArrayPrint](/en/book/common/arrays/arrays_print)).
The string with the d array description contains an error: Entity of initial values exceeds the array size.
@@ -0,0 +1,16 @@
# Expressions
Expressions are essential elements of any programming language. Whatever applied idea underlies an algorithm, it is eventually reduced to data processing, that is, to computations. The expression describes computing some result from one or more predefined values. The values are called operands, while the actions performed with them are denoted by operations or operators.
As operators that allow manipulating with operands, independent characters or their sequences are used in expressions, such as '+' for addition or '*' for multiplication. They all form several groups, such as arithmetic, bitwise, comparison, logic, and some specialized ones.
We have already used expressions in the previous sections of this book, such as to initialize variables. In the simplest case, the expression is a constant (literal) that is the only operand, while the computation result is equal to the operand value. However, operands can also be variables, array elements, function call results (for which the function is called directly from the expression), nested expressions, and other entities.
All operators substitute (return) their result into the parent expression, directly into the place where there were operands, which allows combining them making quite complex hierarchic structures. For example, in the following expression, the result of multiplying variables b by c is added to the value of variable a, and then the value obtained will be stored in variable v:
```
v = a + b * c;
```
In this section, we consider the general principles of constructing and computing various expressions, as well as the standard set of operators supported in MQL5 for the built-in types. Later on, in the part dealing with OOP, we will know how operators can be reloaded (redefined) for custom types, i.e., structures and classes, which will allow us to use objects in expressions and perform nonstandard actions with them.
@@ -0,0 +1,77 @@
# Basic concepts
Before proceeding to the specific groups of operators, we should introduce some basic concepts that are inherent in all operators and affect their applicability and behavior in a particular context.
First of all, by the quantity of operands required, operators can be unary and binary. As is clear from the names, unary ones process one operand, while binary operators process two. In the case of binary, the operator is always placed between operands. Among unary ones, there are operators that must be put before the operand and those to be placed after it. For example, the unary minus ('-') operator allows reversing the sign of the value:
```
int x = 10;
int y = -x;  // -10
```
At the same time, there is a binary operator for subtraction using the same character, '-'.
```
int z = x - y; // 10 - -10 -> 20
```
Choosing a correct operator (action) by the compiler in a specific context is determined by the context of using it in the expression.
Each operator is assigned priority. It determines the order, in which operators will be computed in complex expressions where there are multiple operators. Higher-priority operators are computed as the first, while the lowest-priority ones as the last. For instance, in the expression 1 + 2 * 3 there are two operations (addition and multiplication) and three operands. Since multiplication has a priority higher than that of addition, the product of 2 * 3 will be found first, and then it will be added to one.
Later we will provide the full table of operations with priorities.
Additionally, each operator is characterized by the associativity. It can be left or right and determines the order, in which the successive operators having the same priority are executed. For example, expression 10 - 7 - 1 can purely theoretically be computed in two ways:
- Subtract 7 from 10 and then subtract 1 from the resulting 3, which gives 2; or
- Subtract 1 from 7, which gives 6, and then subtract 6 from 10, resulting in 4.
In the first case, computations were performed from left to right, which corresponds with the left associativity; since the subtraction operation is left-associative, indeed, the first answer is correct.
The second option of computations corresponds with the right associativity and won't be used.
Let's consider another example where there are priority and associativity involved simultaneously: 11 + 5 * 4 / 2 + 3. Both types of operations, i.e., addition and multiplication, are executed from left to right. If the priorities were not different, we would get 35, although 24 is the correct answer. Changing associativity for the right would give us 14.
To explicitly redefine priorities in expressions, parentheses can be used, for instance: (11 + 5) * 4 / (2 + 3). What is enclosed in parentheses is computed earlier, and the intermediate result is substituted in the expression to be used in other operations. Groups in parentheses can be nested. For more details, please see section [Grouping with Parentheses](/en/book/basis/expressions/operators_parentheses).
A right-associative operator can be exemplified by the unary operator of logic negation, '!'. Essentially, its task is to make true from false, and vice versa. Like with other unary operators, associativity means in this context, what side of the operator the operand must be placed. Symbol '!' is placed before the operand, i.e., the operand is to the right.
```
int x = 10;
int on_off = !!x;  // 1
```
In this case, logic negation is performed twice: first time regarding variable x (right '!') and the second time regarding the result of the preceding negation (left '!'). Such double negation allows transforming any nonzero value into 1 due to converting into bool and back.
The final table of operations will also show associativity.
Finally, the last but not the least fine point in processing expressions is the order of computing the operands. It should be distinguished from the priority that belongs to the operation, not operands. The order of computing the operands of binary operations is not defined explicitly, which gives the compiler space to optimize the code and enhance its efficiency. The compiler only guarantees that operands will be computed before executing the operation.
There is a limited set of operations, for which the operand evaluation order is defined. Particularly, for logic AND ('&&') and OR ('||') it is from left to right, and the right part may be omitted if it does not affect anything due to the value of the left part. But as far as the [ternary conditional operator](/en/book/basis/expressions/operator_conditional) '?:' goes, the order is even more intricate, since either one or another branch will be calculated upon computing the first conditions, depending on its trueness. See further sections for more details.
Operand evaluation order is illustrated by the situation where there are several [function](/en/book/basis/functions) calls in the expression. For instance, let 4 functions be used in the expression:
```
a() + b() * c() - d()
```
Priority and associativity rules will only be used for the intermediate results of calling these functions, while the calls themselves can be generated by the compiler in any order it "considers to be necessary" based on the source code features and compiler settings. For example, functions b and c involved in multiplication may be called in the order of [b(), c()] or, vice versa, [c(), b()]. If the functions during being executed may affect the same data, their state will be ambiguous upon the expression computation.
A similar problem can be seen when working with arrays and increment operators (see [Increment and Decrement](/en/book/basis/expressions/increment_decrement)).
```
int i = 0;
int a[5] = {0, 1, 2, 3, 4};
int w = a[++i] - a[++i];
```
Depending on whether the left or the right difference operand will be computed as the first, we can get -1 (a[1] - a[2]) or +1 (a[2] - a[1]). Since the MQL5 compiler is ever-improving, there is no guarantee that the current result (-1) will be retained in the future.
To avoid potential issues, it is recommended not to use an operand repeatedly, if it has already been modified in the same expression.
In all expressions, there can usually be operands of different types. This leads to the need to cast them to a certain common type, before performing any actions with them. If there are no explicit typecasts, MQL5 performs the implicit conversion where necessary. Besides, conversion rules are different for different type combinations. Explicit and implicit typecasting is discussed in the [relevant section](/en/book/basis/conversion).
@@ -0,0 +1,88 @@
# Assignment operation
Expression calculation results must usually be stored somewhere. The assignment operator denoted by '=' is intended for this purpose in the language. The name of a variable or an array element is placed to the left of it, in which the result must be stored, while the expression (in fact, the formula for computation) is to the right.
We have already used this operator for the initialization of variables, which is executed only once, during creating them. However, assignment allows changing the values of variables in the course of the algorithm for an arbitrary number of times. For example:
```
int z;
int x = 1, y = 2;
z = x;
x = y;
y = z;
```
Variables x and y were initialized by values 1 and 2, whereupon the auxiliary third variable z and three assignments were used to exchange values x and y.
The assignment operator, like all operators, returns its result into the expression. This enables writing the assignments in a sequence.
```
int x, y, z;
x = y = z = 1;
```
Here, 1 will first be assigned to variable z, then to variable y, and finally to variable x. Obviously, this operator is right-associative, because the value being assigned drifts from right to left in the expression.
We can use the assignment as a part of an expression. But, since its priority is lower than those of all other operators (except for the "comma" one, see [Priorities of Operations](/en/book/basis/expressions/operators_precedence)), it must be enclosed in parentheses (for more details, please see the section on [Grouping with parentheses](/en/book/basis/expressions/operators_parentheses)). This aspect enables situations where mistypes, such as '=' instead of '==', in expressions lead to not executing the statements as intended. See the example of such behavior in the section dealing with [statement ](/en/book/basis/statements/statements_if)[if](/en/book/basis/statements/statements_if).
The assignment operator imposes certain limitations on what can be to the left of '=' and what to the right of it. In programming, these entities aiming to simplify storing are entitled precisely: LValue and RValue (based on Left and Right).
LValue and RValue
 
LValue represents an entity, for which memory is allocated and, therefore, a value can be written in it. Variable and array elements are the known examples of LValue. Upon having studied OOP, we will get to know another representative of this category: Object, in which the assignment operator can be reloaded. A mandatory element of LValue is the presence of an identifier.
 
It should be considered that variables and arrays may be described with the keyword const, and then they cannot act as LValue, because the modification of constants is prohibited.
 
RValue is a temporary value used in an expression, such as a literal or value returned due to a function call or due to computing a fragment of the expression.
 
Category LValue is of expansive nature, i.e., falling within it allows placing the relevant object to the left of '=' but does not prohibit using it, on par with RValue, to the right of '='.
 
Category RValue, over again, is of a limiting nature, i.e., any RValue may only be to the right of '='.
 
As a certain LValue element is used to the right of '=', its identifier, in fact, denotes its current contents placed into the expression formula.
 
However, if an element of LValue is used to the left of '=', its identifier indicates a memory address (cell) where the new value (expression computation result) should be written.
 
Different operators have different limitations regarding whether they can be used for the operands of LValue or RValue. For example, increment '++' and decrement '--' operators (see [Increment and Decrement](/en/book/basis/expressions/increment_decrement)) may only be used with LValue.
Here are some examples of what is and is not allowed to do with assignment operators (script ExprAssign.mq5):
```
// description of variables
const double cx = 123.0;
int x, y, a[5] = {1};
string s;
// assignment
a[2] = 21;       // ok
x = a[0] + a[1] + a[2]; // ok
s = Symbol();    // ok
cx = 0;          // const variable may not be changed
                 // error: 'cx' - constant cannot be modified
5 = y;           //  this number (literal)
                 // error: '5' - l-value required
x + y = 3;       // to the left of RValue (expression computation result)
                 // error: l-value required
Symbol() = "GBPUSD"; // to the left of RValue with the function call result  
                     // error: l-value required
```
The compiler returns an error of breaking the operator use rules.
@@ -0,0 +1,99 @@
# Arithmetic operations
Arithmetic operations include 5 binary ones, i.e., addition, subtraction, multiplication, division, and division modulo, and 2 unary ones, i.e., plus and minus. Symbols used for each of those operations are given in the table below.
In the column containing examples, e1 and e2 are arbitrary subexpressions. Associativity is marked with 'L' (left to right) and 'R' (right to left). The number in the first column can be considered as precedence of executing the operations.
| P | Symbols | Description | Example | A |
| --- | --- | --- | --- | --- |
| 2 | + | Unary plus | +e1 | R |
| 2 | - | Unary minus | -e1 | R |
| 3 | * | Multiplication | e1 * e2 | L |
| 3 | / | Division | e1 / e2 | L |
| 3 | % | Division modulo | e1 % e2 | L |
| 4 | + | Addition | e1 + e2 | L |
| 4 | - | Subtraction | e1 - e2 | L |
Order in the table corresponds with decreasing the priorities: Unary plus and minus are calculated before multiplication and division, while the latter ones, in turn, before addition and subtraction.
```
double a = 3 + 4 * 5; // a = 23
```
In fact, unary plus does not have any effect in calculations, but can be used for a better visualization of the expression. Unary minus reverses the sign of its operand.
Arithmetic operations are used for numeric types or those that can be cast to them. The calculation result is an RValue. In computation, the storage locations of integer operands are often extended up to the "largest" of the integers used or to int (if all integer types were of a smaller size), as well as cast to a common type. More details can be found in the section on [Typecasting](/en/book/basis/conversion).
```
bool b1 = true;
bool b2 = -b1;
```
In this example, variable b1 "expands" to the int type with value 1. Sign reversing gives -1, which in the reverse typecasting to bool gives true (because -1 is not zero). Using logic type in arithmetic computations is not welcome.
Dividing integers gives an integer, that is, the fractional part, if any, is omitted. It can be checked using the script ExprArithmetic.mq5.
```
int a = 24 / 7;      // ok: a = 3
int b = 24 / 8;      // ok: b = 3
double c = 24 / 7;   // ok: c = 3 (!)
```
Although variable c is described as double, there are integers in the expression to initialize it; therefore, the division performed is an integer. To perform a division with a fractional part, at least one operand must be of real type (the second one will also be cast to it).
```
double d = 24.0 / 7; // ok: d = 3.4285714285714284
```
Operator '%' calculates the remainder of integer division (it is only applicable to two operands of integer type).
```
int x = 11 % 5;   // ok: x = 1
int y = 11 % 5.0; // no real number can be used
                  // error: '%' - illegal operation use
```
Where operands have different signs, operators '*' and '/' give a negative number. The following rules apply to operator '%':
- if the divisor of operator '%' is negative, the sign "escapes"; and
- if the dividend of operator '%' is negative, the result is negative;
This is easy to check using the alternative calculation of division modulo: m % n = m - m / n * n. It should be kept in mind that division m / n for integers will be rounded; therefore, m / n * n is not equal to m, in the general case.
In section [Characteristics of Arrays](/en/book/basis/arrays/arrays_overview), we delved into the idea that a multidimensional array could be represented by a one-dimensional one due to recalculating the indices of their elements. We also provided the formula to obtain an index through in a one-dimensional array by the coordinates (column number X and row number Y at the string length of N) of the two-dimensional array.
```
index = Y * N + X
```
Operation '%' allows us to perform a more convenient backward calculation, i.e., find X and Y by the index-through:
```
Y = index / N
X = index % N
```
If an unpresentable result NaN (Not A Number, such as infinity, square root of a negative number, etc.) was obtained at some stage during calculating the expression, all subsequent operations with it will also produce a NaN. It can be distinguished from a normal number using the MathIsValidNumber function (see [Mathematical Functions](/en/book/common/maths)).
```
double z = DBL_MAX / DBL_MIN - 1; // inf: Not A Number
```
Here, it is subtracted from the NaN (obtained from division) and gives the NaN again.
Addition operation is defined for strings and performs the concatenation, i.e., combining them.
```
string s = "Hello, " + "world!"; // "Hello, World!"
```
Other operations are prohibited for strings.
@@ -0,0 +1,47 @@
# Increment and decrement
Increment and decrement operators allow writing the increase or decrease of an operand by 1 in a simplified manner. They most frequently occur inside [loops](/en/book/basis/statements/statements_for) to modify indexes when accessing to arrays or other objects supporting enumeration.
The increment is denoted by two consecutive pluses: '++'. Decrement is denoted by two consecutive minuses: '--'.
There are two types of such operators: Prefix and postfix.
Prefix operators, as the name implies, are written before operand (++x, --x). They change the operand value, and this new value is involved in the further calculations of the expression.
Postfix operators are written after operand (x++, x--). They substitute the copy of the current operand value in the expression and then change its value (the new value does not get into the expression). Simple examples are given in the script ExprIncDec.mq5.
```
int i = 0, j;
j = ++i;       // j = 1, i = 1
j = i++;       // j = 1, i = 2
```
Postfix form may be useful for more compact writing of expressions combining a reference to the preceding value of the operand and its side modification (two separate statements would be required to make an alternative record of the same). In all other cases, it is recommended to use the prefix form (it does not create a temporary copy of the "old" value).
In the following example, the sign is reversed in the array elements consecutively, until the zeroth element is found. Moving through the array indices is ensured by postfix increment k++ inside [the loop ](/en/book/basis/statements/statements_while)[while](/en/book/basis/statements/statements_while). Due to postfix, expression a[k++] = -a[k] first updates the kth element and then increases k by 1. Then the assignment result is checked for not being equal to zero (!= 0, see [the following section](/en/book/basis/expressions/operators_relational)).
```
int k = 0;
int a[] = {1, 2, 3, 0, 5};
while((a[k++] = -a[k]) != 0){}
// a[] = {-1, -2, -3, 0, 5};
```
The table below shows the increment and decrement operators in order of priority:
| P | Symbols | Description | Example | A |
| --- | --- | --- | --- | --- |
| 1 | ++ | Postfix increment | e1++ | L |
| 1 | -- | Postfix decrement | e1-- | L |
| 2 | ++ | Prefix increment | ++e1 | R |
| 2 | -- | Prefix decrement | --e1 | R |
All increment and decrement operations have a priority higher than arithmetic operations. Prefixes are of a lower priority than postfixes. In the following example, the "old" value of x is summed up with the value of y, upon which x is incremented. If the prefix priority were higher, the increment of y would be performed, upon which the new value, 6, would be summed up with x, and we would get z = 6, x = 0 (previous).
```
int x = 0, y = 5;
int z = x+++y; // "x++ + y" : z = 5, x = 1
```
@@ -0,0 +1,87 @@
# Comparison operations
As the name implies, these operations are intended for comparing two operands and returning a logic feature, true or false, depending on the condition to hold in the comparison.
The table below gives all comparison operations and their properties, such as symbols used, priorities, examples, and associativity.
| P | Symbols | Description | Example | A |
| --- | --- | --- | --- | --- |
| 6 | < | Less | e1 < e2 | L |
| 6 | > | Greater | e1 > e2 | L |
| 6 | <= | Less than or equal | e1 <= e2 | L |
| 6 | >= | greater than or equal | e1 >= e2 | L |
| 7 | == | Equal | e1 == e2 | L |
| 7 | != | Not equal | e1 != e2 | L |
The principle of each operation is to compare two operands using the criterion from the column containing its description. For example, entry "x < y" means checking whether "x is lesser than y". Correspondingly, the comparison result will be true if x is really lesser than y, and false in all other cases.
Comparisons work for the operands of any type (for different types, [typecasting](/en/book/basis/conversion/conversion_implicit) is performed).
Considering the left associativity and the return of the bool type result, constructing a sequence of comparisons does not work so obviously. For example, a hypothetic expression to check whether the value y lies between the values of x and z, could seemingly appear as follows:
```
int x = 10, y = 5, z = 2;
bool range = x < y < z;   // true (!)
```
However, such an expression is processed in a different manner. Even the compiler distinguishes it by the warning: "unsafe use of type 'bool' in operation".
Due to the left associativity, the left condition x < y is checked first, and its result is substituted as a temporary value of the bool type into the expression that goes as follows: b < z. Then the value of z is compared to true or false in the temporary variable b. To check whether y ranges between x and z, you should use two comparison operations combined with the logic operation AND (it will be considered in the [next section](/en/book/basis/expressions/operators_logical)).
```
int x = 10, y = 5, z = 2;
bool range = x < y && y < z;   // false
```
When using the comparing for equality/inequality, the features of the operand types shall be considered. For instance, floating-point numbers often contain "approximate" values after calculations (we considered the accuracy of representing double and float in the section [Real Numbers](/en/book/basis/builtin_types/float_numbers)). For example, the sum of 0.6 and 0.3 is not strictly 0.9:
```
double p = 0.3, q = 0.6;
bool eq = p + q == 0.9;        // false
double diff = p + q - 0.9;     // -0.000000000000000111
```
The difference makes 1*10-16, but it is sufficient for the comparison operation to return false.
Therefore, real numbers should be compared for equality/inequality using the greater-/less-then operators for their difference and acceptable deviation that is sorted out manually, based on the features of the computation, or a universal one is taken. Recall that for double and float, the embedded accuracy constants, DBL_EPSILON and FLT_EPSILON, are defined, valid for the value of 1.0. They must be scaled to compare other values. In script ExprRelational.mq5, one of the possible realizations of function isEqual is presented to compare real numbers, which considers this aspect.
```
bool isEqual(const double x, const double y)
{
   const double diff = MathAbs(x - y);
   const double eps = MathMax(MathAbs(x), MathAbs(y)) * DBL_EPSILON;
   return diff < eps;
}
```
Here we use the function of obtaining an absolute unsigned value (MathAbs) and the highest of the two values (MathMax). They will be described in the section [Mathematical Functions](/en/book/common/maths) of Part 4. The absolute difference between the parameters of function isEqual is compared to the calibrated tolerance in variable eps using operation '<'.
This function cannot be used to compare with absolute zero, anyway. For this purpose, you can use the following approach (it will probably require some adaptation to your specific needs):
```
bool isZero(const double x)
{
   return MathAbs(x) < DBL_EPSILON;
}
```
Strings are compared lexicographically, i.e., letter by letter. The code of each character is compared to the code of the character in the same position of the second string. Comparison is performed until a difference in the codes is found or one of the strings ends. The string ratio will be equal to that of the first differing characters, or a longer string will be considered greater than the shorter one. Remember that upper- and lowercase letters have different codes, and strange enough, uppercase ones have smaller codes than the lowercase ones.
An empty string "" (in fact, it stores one terminal 0) is not equal to the special value of NULL which means no string.
```
bool cmp1 = "abcdef" > "abs";     // false, [2]: 's' > 'c'
bool cmp2 = "abcdef" > "abc";     // true,  by length
bool cmp3 = "ABCdef" > "abcdef";  // false, by case
bool cmp4 = "" == NULL;           // false
```
Moreover, to compare strings, MQL5 provides some functions that will be described in the section [Working with Strings](/en/book/common/strings).
In comparing for equality/inequality, it is not recommended to use bool constants: true or false. The matter is that, in expressions like v == true or v == false, operand v can be interpreted intuitively as a logical type, while in fact, it is a number. As it is known, zero value is considered false in numbers, while all others are interpreted as true (we often want to use it as an indication of some result being present or absent). However, in this case, typecasting goes backward: true or false are "expanded" to a numeric type v and actually become equal to 1 and 0, respectively. Such a comparison will have a result other than the expected one (for example, comparison 100 == true will turn out to be false).
@@ -0,0 +1,59 @@
# Logical operations
Logical operations perform computations on logical operands and return a result of the same type.
| P | Symbols | Description | Example | A |
| --- | --- | --- | --- | --- |
| 2 | ! | Logical NOT | !e1 | R |
| 11 | && | Logical AND | e1 && e2 | L |
| 12 | || | Logical OR | e1 || e2 | L |
Logical NOT transforms true into false and false into true.
Logical AND is equal to true if both operands are equal to true.
Logical OR is equal to true if at least one operand is equal to true.
Operators AND and OR always compute operands from left to right and, if possible, use the computational shortcut. If the left operand is equal to false, then operator AND skips the second operand, because it does not affect anything the result is already false. If the left operand is equal to true, then operator OR skips the second operand for the same reason, since the result will, in any case, be equal to true.
This is often used in programs to prevent from errors in the second (and subsequent) operands. For example, we can hedge ourselves against the error of accessing a non-existing array element:
```
index < ArraySize(array) && array[index] != 0
```
Here we use the built-in function ArraySize that returns the array length. Only if index is smaller than the length, the element with this index is read and compared with zero.
Checking by contraries, using '||' is also used, for example:
```
ArraySize(array) == 0 || array[0] == 0
```
The condition is true immediately if the array is null. And only if there are elements, the additional check for the contents will continue.
If the expression consists of multiple operands combined by logical OR, then with the first true (if any) the total result of true will be obtained immediately. However, if operands are combined by logical AND, then with the first false the total result of false will be obtained immediately.
Of course, you can combine different operations within one expression, considering their different priority: Negation is executed first, then the AND-related conditions, and in the end the OR-related conditions. If another sequence is required, it must be explicitly specified using parentheses.
For example, the following expression without parentheses, A && B || C && D, is in fact equivalent to: (A && B) || (C && D). For the logical OR to be executed as the first, it should be enclosed in parentheses: A && (B || C) && D. For more details on using parentheses, see section [Grouping with Parentheses](/en/book/basis/expressions/operators_parentheses).
Simple examples are given in script ExprLogical.mq5 to check logical operations in practice.
```
int x = 3, y = 4, z = 5;
bool expr1 = x == y && z > 0;  // false, x != y, z does not matter
bool expr2 = x != y && z > 0;  // true,  both conditions are complied with
bool expr3 = x == y || z > 0;  // true,  it is sufficient that z > 0
bool expr4 = !x;               // false, x must be 0 to get true
bool expr5 = x > 0 && y > 0 && z > 0; // true, all 3 are complied with
bool expr6 = x < 0 || y > 0 && z > 0; // true, y and z are sufficient
bool expr7 = x < 0 || y < 0 || z > 0; // true, z is sufficient
```
In the string of calculating expr6, the compiler gives the warning: "Check operator precedence for possible error; use parentheses to clarify precedence".
Logical operations '&&' and '||' should not be mixed with bitwise operations '&' and '|' (considered in the [next section](/en/book/basis/expressions/operators_bitwise)).
@@ -0,0 +1,70 @@
# Bitwise operations
Sometimes you may need to process numbers at the bit level. For this purpose, there is a group of bitwise operations applicable to integer types.
All symbols and descriptions of bitwise operators are provided with their associativity and in order of their priority in the table below.
| P | Symbols | Description | Example | A |
| --- | --- | --- | --- | --- |
| 2 | ~ | Bitwise complement (inversion) | ~e1 | R |
| 5 | << | Shift to the left | e1 << e2 | L |
| 5 | >> | Shift to the right | e1 >> e2 | L |
| 8 | & | Bitwise AND | e1 & e2 | L |
| 9 | ^ | Bitwise exclusive OR | e1 ^ e2 | L |
| 10 | | | Bitwise OR | e1 | e2 | L |
Of the entire group, only the bitwise complement operation '~' is unary, while all others are binary.
In all cases, if the operand size is less than int/uint, it is preliminarily extended to int/uint by adding 0 bits into higher order. Based on the operand type being signed/unsigned, a high-order bit may affect the sign.
Standard Windows application, Calculator, may help understand the representation of numbers at the bit level. If you select the Programmer operation mode in the View menu, the groups of toggle buttons will appear in the program to select representing the number in a hexadecimal (Hex), decimal (Dec), octal (Oct), or binary (Bin) form. It is the latter one that shows bits. Moreover, you can select the number size: 1, 2, 4, and 8 bytes. The buttons allow executing all the operations considered: Not ('~'), And ('&'), Or ('|'), Xor ('^'), Lsh ('<<'), and Rsh ('>>').
 
Since the Calculator uses signed numbers, negative values may appear when toggling to the decimal mode (remember that the high-order bit is interpreted as a sign). For convenient analysis, it is reasonable to exclude the minus that appears, for which purpose it is necessary to select the size in bytes one grade higher. For example, to check the values within the range up to 255 (uchar, unsigned one-byte integer), you should select 2 bytes (otherwise, only decimal values through 127 will be positive, while the others will be displayed in the negative region).
Bitwise complement creates a value, in which the 0-bit is in the place of all 1-bits, while 1-bit is in the place of 0-bits. For example, the negation of a byte with all zero bits gives a byte with all 1 bits. Number 50 appears in the bitwise format as '00110010' (byte). Its inversion gives '11001101'.
Unity represented hexadecimally is 0x0001 (for short). Inversion of these bits gives 0xFFFE (see script ExprBitwise.mq5).
```
short v = ~1;  // 0xfffe = -2
ushort w = ~1; // 0xfffe = 65534
```
Bitwise AND checks each bit in both operands and in the positions where two set bits (1) are found, stores the 1-bit into the result. In all other cases (where there is only a set bit in one operand or they are reset in both places), the 0-bit is written in the result.
Bitwise OR writes 1-bits into the result if they are on the positions where there is a set bit in at least one of two operands.
Bitwise exclusive OR writes in the result the 1-bits on the positions where there is a set bit in either the first or second operand, but not in both at the same time. The binary representation of two numbers, X and Y, and the results of bitwise operations with them are shown below.
```
X       10011010   154
Y       00110111    55
 
X & Y   00010010    18
X | Y   10111111   191
X ^ Y   10101101   173
```
When writing complex expressions from several different operators, use grouping with parentheses in order not to become confused with priorities.
Shift operations move bits to the left ('<<') or right ('>>') by the quantity of bits, defined in the second operand that must be a non-negative integer. As a result, left (for '<<') or right (for '>>') bits are dropped, since they go beyond the memory cell boundaries. With the left shift, the relevant number of 0 bits are added on the right. With the right shift, either 0 bits are added on the left (if the operand is unsigned) or the sign bit is reproduced (if the operand is signed). In the latter case, 0 bits are added on the left for positive numbers and 1 bits for negative ones; i.e., the sign retains.
```
short q = v << 5;  // 0xffc0 = -64
ushort p = w << 5; // 0xffc0 = 65472
short r = q >> 5;  // 0xfffe = -2
ushort s = p >> 5; // 0x07fe = 2046
```
In the example above, the initial left shift "destroyed" the high-order bits of variable p, while the subsequent right shift by the same quantity of bits filled them with zeros, which led to decreasing the value from 0xffc0 to 0x07fe.
Shift size (quantity of bits) must be less than that of the operand type (considering its potential extension). Otherwise, all initial bits will get lost.
Shifting by 0 bits leaves the number unchanged.
Bitwise operations '&' and '|' should not be mixed with logical operations '&&' and '||' (considered in the [preceding section](/en/book/basis/expressions/operators_logical)).
@@ -0,0 +1,55 @@
# Modification operations
Modification that is also called compound assignment allows combining within one operator [arithmetic](/en/book/basis/expressions/operators_arithmetic) or [bitwise](/en/book/basis/expressions/operators_bitwise) operations with normal [assignment](/en/book/basis/expressions/operator_assignment).
| P | Symbols | Description | Example | A |
| --- | --- | --- | --- | --- |
| 14 | += | Addition with assignment | e1 += e2 | R |
| 14 | -= | Subtraction with assignment | e1 -= e2 | R |
| 14 | *= | Multiplication with assignment | e1 *= e2 | R |
| 14 | /= | Division with assignment | e1 /= e2 | R |
| 14 | %= | Division modulo with assignment | e1 %= e2 | R |
| 14 | <<= | Left shift with assignment | e1 <<= e2 | R |
| 14 | >>= | Right shift with assignment | e1 >>= e2 | R |
| 14 | &= | Bitwise AND with assignment | e1 &= e2 | R |
| 14 | |= | Bitwise OR with assignment | e1 |= e2 | R |
| 14 | ^= | Bitwise AND/OR with assignment | e1 ^= e2 | R |
These operators execute the relevant action for operands e1 and e2, whereupon the result is stored in e1.
An expression like e1 @= e2 where @ is any operator from the table is approximately equivalent to e1 = e1 @ e2. The word "approximately" emphasizes the presence of some subtle aspects.
First, if the place of e2 is occupied by an expression with an operator having a lower priority than that of @, e2 is still calculated before that. That is, if the priority is marked with parentheses, we will get e1 = e1 @ (e2).
Second, if there are side modifications of variables in expression e1, they are made only once. The following example demonstrates this.
```
int a[] = {1, 2, 3, 4, 5};
int b[] = {1, 2, 3, 4, 5};
int i = 0, j = 0;
a[++i] *= i + 1;           // a = {1, 4, 3, 4, 5}, i = 1
                           // not equivalent!
b[++j] = b[++j] * (j + 1); // b = {1, 2, 4, 4, 5}, j = 2
```
Here, arrays a and b contain identical elements and are processed using index variables i and j. At the same time, the expression for array a uses operation '*=', while that for array b uses the equivalent. Results are not equal: Both index variables and arrays differ.
Other operators will be useful in problems with bit-level manipulations. Thus, the following expression can be used to set a specific bit into 1:
```
ushort x = 0;
x |= 1 << 10;
```
Here, shift 1 ('0000 0000 0000 0001') is made by 10 bits to the left, which results in obtaining a number with one set 10th bit ('0000 0100 0000 0000'). Bitwise OR operation copies this bit into variable x.
To reset the same bit, we will write:
```
x &= ~(1 << 10);
```
Here, the inversion operation is applied to 1 shifted by 10 bits to the left (which we saw in the preceding expression), which results in all bits changing their value: '1111 1011 1111 1111'. Bitwise AND operation resets the zeroed bits (in this case, one) in variable x, while all other bits in x remain unchanged.
@@ -0,0 +1,97 @@
# Conditional ternary operator
Conditional ternary operator allows describing in a single expression two calculation options, based on a certain condition. The operator syntax is as follows:
```
condition ? expression_true : expression_false
```
The logical condition must be specified in the first operand 'condition'. This can be an arbitrary combination of [comparison operations](/en/book/basis/expressions/operators_relational) and [logical operations](/en/book/basis/expressions/operators_logical). Both branches must be present.
If the condition is true, expression expression_true will be computed, while if it is false, the expression_false will be computed.
This operator guarantees that only one of the expressions expression_true and expression_false will be executed.
Types of the two expressions must be identical, otherwise, there will be an attempt to [implicitly typecast](/en/book/basis/conversion/conversion_implicit) them.
Please note that the result of processing expressions in MQL5 always represents an RValue (in C++, if only LValues are in expressions, then the result of the operator will also be LValue). Thus, the following code is compiled well in C++, but gives an error in MQL5:
```
int x1, y1; ++(x1 > y1 ? x1 : y1); // '++' - l-value required
```
Conditional operators can be nested, that is, it is permitted to use another conditional operator as a condition or either branch (expression_true or expression_false). At the same time, it cannot be always clear what the conditions relate to (if parentheses are not used to explicitly denote grouping). Let's consider examples from ExprConditional.mq5.
```
int x = 1, y = 2, z = 3, p = 4, q = 5, f = 6, h = 7;
int r0 = x > y ? z : p != 0 && q != 0 ? f / (p + q) : h; // 0 = f / (p + q)
```
In this case, the first logical condition represents comparison x > y. If it is true, the branch with variable z is executed. If it is false, the additional logical condition p != 0 && q != 0 is checked, with two expression options, as well.
Below are some more operators, in which logical conditions are written uppercase, while computation options are lowercase. For simplicity, they all are made variables (from the example above). In reality, each of the three components may be a richer expression.
For each string, you can track how the result is obtained, which has been shown in the comment.
```
bool A = false, B = false, C = true;
int r1 = A ? x : C ? p : q;                              // 4
int r2 = A ? B ? x : y : z;                              // 3
int r3 = A ? B ? C ? p : q : y : z;                      // 3
int r4 = A ? B ? x : y : C ? p : q;                      // 4
int r5 = A ? f : h ? B ? x : y : C ? p : q;              // 2
```
Since the operator is right-associative, the compound expression is analyzed from right to left, that is, the rightmost structure with three operands combined by '?' and ':' becomes the operand of the external condition written to the left. Then, considering this substitution, the expression is analyzed from right to left again, and so on, until the final complete upper-level structure '?:' is obtained.
Therefore, the expressions above are grouped as follows (parentheses denote the implicit interpretation of the compiler; but such parentheses could be added into expressions to visualize the source code, which approach is actually recommended).
```
int r0 = x > y ? z : ((p != 0 && q != 0) ? f / (p + q) : h);
int r1 = A ? x : (C ? p : q); 
int r2 = A ? (B ? x : y) : z; 
int r3 = A ? (B ? (C ? p : q) : y) : z; 
int r4 = A ? (B ? x : y) : (C ? p : q); 
int r5 = (A ? f : h) ? (B ? x : y) : (C ? p : q); 
```
For variable r5, the first condition A ? f : h computes the logical condition for the subsequent expression and therefore, is transformed into bool. Since A is equal to false, the value is taken from variable h. It is not equal to 0; therefore, the first condition is considered true. This results in the actuating branch (B ? x : y), from which the value of variable y is returned, since B is equal to false.
There must be all 3 components (a condition and 2 alternatives) in the operator. Otherwise, the compiler will generate the error "unexpected token":
```
// ';' - unexpected token
// ';' - ':' colon sign expected
int r6 = A ? B ? x : y; // lack of alternative
```
In the compiler language, a token is an indivisible fragment of the source code, having its independent meaning or purpose, such as type, identifier, punctuation character, etc. The entire source code is divided by the compiler into a sequence of tokens. Signs of the operators considered are tokens, too. In the code above, there are two symbols '?', and there must be two symbols ':' matching with them, but it is the only one. Therefore, the compiler "says" that the statement end symbol ';' is premature and "inquires" what exactly is deficient: "colon sign expected".
Since the conditional operator has a very low priority (13 in the full table, see [Priorities of Operations](/en/book/basis/expressions/operators_precedence)), it is recommended to enclose it in parentheses. This makes it easier to avoid situations where the operands of a conditional operator could be "caught" by the neighboring operations having higher priorities. Fir instance, if we need to calculate the value of a certain variable w via the sum of two ternary operators, a straightforward approach might appear as follows:
```
int w = A ? f : h + B ? x : y;                           // 1
```
This will work differently than we thought. Due to the higher priority, the sum h + B is considered as a single expression. Considering its parsing from right to left, this sum appears as a condition and is cast to the bool type, which is even warned by the compiler as "expression not boolean". Compiler interpretation can even be visualized by parentheses:
```
int w = A ? f : ((h + B) ? x : y);                       // 1
```
To solve the problem, we should place parentheses in our own way.
```
int v = (A ? f : h) + (B ? x : y);                       // 9
```
Deep nesting of conditional operators impacts adversely on the code understandability. Nesting levels exceeding two or three should be avoided.
@@ -0,0 +1,27 @@
# Comma
Operator comma that is explicitly denoted as ',' is placed between two expressions computed independently from left to right. In other words, this operator does not perform any actions itself but just allows specifying the sequence of two or more expressions within a statement.
Expressions placed right-hand in the sequence can use the results of computing the left-hand expressions, since they have already been processed.
The operator result is the result of the rightmost expression. The operator has the lowest priority.
Currently, using the operator in MQL5 is limited by the header of the [for statement](/en/book/basis/statements/statements_for).
Example:
```
for(i=0,j=99; i<100; i++,j--) 
   Print(array[i][j]);
```
Let's repeat the key aspects of the comma operator in MQL5:
Order of evaluation:
- Expressions are processed from left to right. Thus, the expressions on the right can use the results of the expressions on the left since they have already been processed.
Result and priority:
- The result of the comma operator is the value of the rightmost expression. It's important to note that the comma operator has the lowest priority, meaning that other operators in the expression may have higher priorities.
@@ -0,0 +1,51 @@
# Special operators sizeof and typename
sizeof
The sizeof operator returns the size of its operand in bytes. Operator syntax: sizeof(x), where x can be a type or an expression. The expression is not computed in this case, since operator sizeof is executed at the compilation stage and, in fact, a constant is substituted in its place in the expression.
For fixed-size arrays, the operator returns the total amount of the allocated memory, that is, the multiplication of the number of elements in all dimensions by the type size in bytes. For dynamic arrays, it returns the size of an internal structure storing the array properties.
Let's give some examples with explanations (ExprSpecial.mq5).
```
double array[2][2];
double dynamic1[][1];
double dynamic2[][2];
Print(sizeof(double));                           // 8
Print(sizeof(string));                           // 12
Print(sizeof("This string is 29 bytes long!"));  // 12
Print(sizeof(array));                            // 32
Print(sizeof(array) / sizeof(double));           // 4 (quantity of elements)
Print(sizeof(dynamic1));                         // 52
Print(sizeof(dynamic2));                         // 52
```
The result to be printed in the log is marked in the comments.
Type double takes up 8 bytes. The size of the string type is 12. These 12 bytes store the service information we mentioned in the section dealing with type [string](/en/book/basis/builtin_types/strings). This memory is allocated for any string (even uninitialized). Please note that a string containing a 29-character text is also sized 12. This is because both an empty string and a string with some contents have an internal structure intended for storing a reference to memory. To obtain the text length, we should use the [StringLen](/en/book/common/strings/strings_init) function.
Fixed-size array size is really computed as the multiplication of the number of elements (2*2=4) by the double type size (8), a total of 32. As a consequence, an expression like sizeof(array) / sizeof(double) allows finding out the entity of elements in it.
For dynamic arrays, the internal structure size is 52 bytes. Differences in the descriptions of arrays dynamic1 and dynamic2 do not affect this value.
Operator sizeof is especially useful to get the sizes of [classes](/en/book/oop/classes_and_interfaces) and [structures](/en/book/oop/structs_and_unions).
typename
Operator typename returns a string with the name of the parameter passed to it, which can be a type or an expression. For arrays, along with the data type keyword, a tag is printed as a pair of parentheses (or several ones, depending on the array dimensionality).
```
Print(typename(double));                         // double
Print(typename(array));                          // double [2][2]
Print(typename(dynamic1));                       // double [][1]
Print(typename(1 + 2));                          // int
```
For custom types, such as classes, structures, and others (that we will consider in Part 3), the type name follows the entity category, such as "class MyCustomType". Moreover, for constants, the "const" modifier will be added to the string description.
Therefore, to know the short type name consisting of one word, use macro TYPENAME from the attached file TypeName.mqh.
It can be necessary to learn the type name in the so-called [templates](/en/book/oop/templates) that can generate from the source code similar realizations for different types defined in the parameters of templates.
@@ -0,0 +1,20 @@
# Grouping with parentheses
In the preceding sections, we have already seen more than a few times that some expressions can cause unexpected results due to the priorities of operations. To explicitly change the computation order, we should use parentheses. Part of the expression enclosed in them gets a higher priority as compared to the environment, without regard to default priorities. Pairs of parentheses can be nested, but it is not recommended to make more than 3-4 nesting levels. It is better to divide the too complex expressions into several simpler ones.
Script ExprParentheses.mq5 shows the evolution of placing parentheses within one expression. The initial intent for it is to set the bit in variable flags using the left-shift operation '<<'. The bit number is taken from variable offset if it is not zero, or otherwise, as 1 (remember that numbering starts with zero). Then the obtained value is multiplied by coefficient. No need to search for any applied sense in this example. However, more sophisticated structures can occur, too.
```
int offset = 8;
int coefficient = 10, flags = 0;
int result1 = coefficient * flags | 1 << offset > 0 ? offset : 1;     // 8
int result2 = coefficient * flags | 1 << (offset > 0 ? offset : 1);   // 256
int result3 = coefficient * (flags | 1 << (offset > 0 ? offset : 1)); // 2560
```
The first version, without parentheses, seems suspicious even to the compiler. It gives a warning that we have already known: "expression not boolean". The matter is that the ternary conditional operator has the lowest priority of all operators here. For this reason, the entire left part before '?' is considered its condition. Inside the condition, calculations are in the following order: Multiplication, bitwise shift, "more than" comparison, and bitwise OR, which results in an integer. Of course, it can be used as true or false, but it is desired to "communicate" such intentions to the compiler using [explicit typecasting](/en/book/basis/conversion/conversion_explicit). If it is absent, the compiler considers the expression suspicious, and not in vain. The first calculation results in 8. It is incorrect.
Let's add parentheses around the ternary operator. The warning of the compiler will disappear. However, the expression is still computed wrongly. Since the priority of multiplication is higher than that of bitwise OR, variables coefficient and flags are multiplied before the bit mask is used, which is obtained by shifting to the left. The result is 256.
Finally, having added another pair of parentheses, we will get the correct result: 2560.
@@ -0,0 +1,61 @@
# Priorities of operations
Here is the full table of all operations in the order of their priorities.
| P | Symbols | Description | Example | A |
| --- | --- | --- | --- | --- |
| 0 | :: | Scope resolution | n1 :: n2 | L |
| 1 | () | Grouping | (e1) | L |
| 1 | [] | Index | [e1] | L |
| 1 | . | Dereferencing | n1.n2 | L |
| 1 | ++ | Postfix increment | e1++ | L |
| 1 | -- | Postfix decrement | e1-- | L |
| 2 | ! | Logical NOT | !e1 | R |
| 2 | ~ | Bitwise complement (inversion) | ~e1 | R |
| 2 | + | Unary plus | +e1 | R |
| 2 | - | Unary minus | -e1 | R |
| 2 | ++ | Prefix increment | ++e1 | R |
| 2 | -- | Prefix decrement | --e1 | R |
| 2 | (type) | Typecasting | (n1) | R |
| 2 | & | Taking the address | &n1 | R |
| 3 | * | Multiplication | e1 * e2 | L |
| 3 | / | Division | e1 / e2 | L |
| 3 | % | Division modulo | e1 % e2 | L |
| 4 | + | Addition | e1 + e2 | L |
| 4 | - | Subtraction | e1 - e2 | L |
| 5 | << | Shift to the left | e1 << e2 | L |
| 5 | >> | Shift to the right | e1 >> e2 | L |
| 6 | < | Less | e1 < e2 | L |
| 6 | > | Greater | e1 > e2 | L |
| 6 | <= | Less than or equal | e1 <= e2 | L |
| 6 | >= | Greater than or equal | e1 >= e2 | L |
| 7 | == | Equal | e1 == e2 | L |
| 7 | != | Not equal | e1 != e2 | L |
| 8 | & | Bitwise AND | e1 & e2 | L |
| 9 | ^ | Bitwise exclusive OR | e1 ^ e2 | L |
| 10 | | | Bitwise OR | e1 | e2 | L |
| 11 | && | Logical AND | e1 && e2 | L |
| 12 | || | Logical OR | e1 || e2 | L |
| 13 | ?: | Conditional ternary | c1 ? e1 : e2 | R |
| 14 | = | Assignment | e1 = e2 | R |
| 14 | += | Addition with assignment | e1 += e2 | R |
| 14 | -= | Subtraction with assignment | e1 -= e2 | R |
| 14 | *= | Multiplication with assignment | e1 *= e2 | R |
| 14 | /= | Division with assignment | e1 /= e2 | R |
| 14 | %= | Division modulo with assignment | e1 %= e2 | R |
| 14 | <<= | Left shift with assignment | e1 <<= e2 | R |
| 14 | >>= | Right shift with assignment | e1 >>= e2 | R |
| 14 | &= | Bitwise AND with assignment | e1 &= e2 | R |
| 14 | |= | Bitwise OR with assignment | e1 |= e2 | R |
| 14 | ^= | Bitwise AND/OR with assignment | e1 ^= e2 | R |
| 15 | , | Comma | e1 , e2 | L |
As we have seen, square brackets are used to specify the indices of array elements and, therefore, have one of the highest priorities.
Along with operators that have been considered earlier, there are some still unknown ones here.
We will learn the [scope resolution](/en/book/oop/classes_and_interfaces/classes_namespace_context) operator '::' within object-oriented programming (OOP). We will also need the dereferencing operator '.' at the same time. Identifiers of types (classes) and their properties, not expressions, act as their operands.
Address-taking operator '&' is intended to pass the [function parameters by referencing](/en/book/basis/functions/functions_ref_value) and to obtain the [object addresses](/en/book/oop/classes_and_interfaces/classes_pointers) in OOP. In both cases, the operator is applied to a variable (LValue).
Explicit typecasting operations will be considered in the [next chapter](/en/book/basis/conversion/conversion_explicit).
@@ -0,0 +1,19 @@
# Type conversion
In this section, we will consider the concept of type conversion, limiting ourselves to built-in data types for now. Later, after studying OOP, we will supplement it with the nuances inherent in object types.
Type conversion in MQL5 is the process of changing the data type of a variable or expression. MQL5 supports three main types of type conversion: implicit, arithmetic, and explicit.
[Implicit type conversion](/en/book/basis/conversion/conversion_implicit):
- Occurs automatically when a variable of one type is used in a context that expects another type. For example, integer values can be implicitly converted to real values.
[Arithmetic type conversion](/en/book/basis/conversion/conversion_arithmetic):
- Arises during arithmetic operations with operands of different types. The compiler attempts to maintain maximum accuracy but warns about potential data loss. For instance, in integer division, the result is converted to a real type.
[Explicit type conversion](/en/book/basis/conversion/conversion_explicit):
- Gives the programmer control over type conversion. It is done in two forms: C-style ((target)) and "functional" style (target()). It is used when you need to explicitly instruct the compiler to perform a conversion between types, for example, when rounding real numbers or when successive type conversions are required.
Understanding the differences between implicit, arithmetic, and explicit type conversion is crucial for ensuring the correct execution of operations and avoiding data loss. This knowledge helps programmers effectively utilize this mechanism in MQL5 development.
@@ -0,0 +1,40 @@
# Implicit type conversion
Type conversion occurs automatically if one type is used at some point in the source code, but another is expected, and there are conversion rules between them. Such conversion is called an implicit type conversion and may not always correspond to the programmer's intent. In addition, some conversion operations have side effects, and the compiler, not knowing whether their use is intentional, highlights the corresponding lines of code with warnings. To solve these problems, there is an explicit type conversion syntax (see [Explicit type casting](/en/book/basis/conversion/conversion_explicit)).
We have already seen several rules for implicit type conversion while studying types and variables.
Specifically, if a value of type other than boolean is assigned to a bool variable, then the value 0 is regarded as false, and all the rest as true. In the more general case, all expressions that assume the presence of logical conditions are converted to type bool. For example, the first operand of a ternary conditional operator is always converted into a bool.
But if a value of type bool is assigned to a numeric type, then true becomes 1, and false becomes 0.
When a real number is assigned to an integer type variable, the fractional part is discarded (the compiler issues a warning). When an integer, on the other hand, is assigned to a variable of real type, precision can be lost (the compiler also issues a warning). We have already talked about this in the sections on [Integer numbers](/en/book/basis/builtin_types/integer_numbers) and [Real numbers](/en/book/basis/builtin_types/float_numbers).
If we have integer and floating point numbers, everything is converted to floating point numbers of the maximum size used (usually double, unless you explicitly specify float or the numeric literal has a suffix 'f', for example1234.56789f).
For integers of different sizes, there are also conversion rules: they expand if necessary, which means that they increase to the size of the largest integer type used in the expression (see [Arithmetic type conversions](/en/book/basis/conversion/conversion_arithmetic)).
In addition to expressions, we often need to implicitly convert types during initialization and assignment, when the types to the right and left of the '=' sign do not match. The same conversion rules apply when passing values through function parameters and returning results from functions (for further details please see the [Functions](/en/book/basis/functions) section).
Considering the above, a large number of conversions can be performed in one line of code. If this causes compiler warnings, it's a good idea to make sure the conversion is intentional and eliminate warnings by inserting an explicit type conversion.
```
short s = 10;
long n = 10;
int p = s * n + 1.0;
```
In this example, when performing a multiplication, the type of the variable s is extended to the type of the second operand long and an intermediate result of type long is obtained. Because the constant 1.0 is of type double, the result of the product is converted to double before addition. The overall result is also of type double; however, the variable p is of type int and therefore an implicit conversion from double to int is performed.
The special types datetime and color are processed according to the rules of integers with lengths of 8 and 4 bytes, respectively. But for date and time, there is a stricter limit on the maximum value - 32535244799, which corresponds to D'3000.12.31 23:59:59'.
Most types can be implicitly converted to and from strings, but the results are not always adequate, so the compiler issues warnings "implicit conversion from 'number' to 'string'" and "implicit conversion from 'string' to 'number'" so that the programmer can check them. For example, converting a string to an integer allows the string to contain only digits and '+'/'-' characters at the beginning. Converting from a string to a real allows, in addition to numbers, the presence of a dot '.' and notation with "exponent" ('e' or 'E', e.g. +1.2345e-1). If an unsupported character (for example, a letter) is encountered in the string, the rest of the string is discarded in full.
For example, the string date and time ("2021.12.12 00:00") cannot be assigned without losses to a variable of type datetime because datetime is an integer (number of seconds). In this case, reading the number from the string will end when the first point is reached, i.e. the number will get the value 2021. This number of seconds corresponds to the 34th minute of the year 1970.
There are special functions for such conversions (see section [Data Transformation](/en/book/common/conversions/conversions_numbers)).
The only direction of implicit and explicit type conversion that is forbidden is from string to bool. The compiler in such cases shows the error message "cannot implicitly convert type 'string' to 'bool'".
Examples from this chapter are provided in TypeConversion.mq5.
@@ -0,0 +1,102 @@
# Arithmetic type conversions
In arithmetic calculation and comparison expressions, values of different types are often used as operands. To process them correctly, it is necessary to bring the types to a certain "common denominator". The compiler attempts to do this without the programmer's intervention unless the programmer has specified explicit conversion rules (see [Explicit type conversion](/en/book/basis/conversion/conversion_explicit)). In this case, the compiler, whenever possible, tries to preserve the maximum precision when it comes to numbers. In particular, it produces an increase in the capacity of integer numbers and the transition from integer to real numbers (if they are involved).
Integer expansion implies conversion of bool, char, unsigned char, short, unsigned short to int (or unsigned int if int isn't big enough to store specific numbers). Large values can be converted to long and unsigned long.
If the type of the variable is not able to store the result of the type that was obtained when the expression was evaluated, the compiler will issue a warning:
```
double d = 1.0;
int x = 1.0 / 10; // truncation of constant value
int y = d / 10;   // possible loss of data due to type conversion
```
The expression to initialize the variables x and y contains the real number 1.0, so the other operands (constant 10 in this case) are converted to double, and the result of division will also be of type double. However, the type of variables is int, and therefore an implicit conversion to it takes place.
Calculation 1.0 / 10 is done by the compiler during compilation and therefore it gets a constant of type double (0.1). Of course, in practice, it is unlikely that the initializing constant will exceed the size of the receiving variable. Therefore, the compiler warning "truncation of constant value" can be considered exotic. It just shows the problem in the most simplified way.
However, as a result of variable-based calculations, similar data loss can also occur. The second compiler warning we see here ("possible loss of data due to type conversion") occurs much more frequently. Moreover, the loss is possible not only when converting from real type to integer, but also vice versa.
```
double f = LONG_MAX; // truncation of constant value
long m1 = 1000000000;
f = m1 * m1;         // possible loss of data due to type conversion
```
As we know, type double cannot accurately represent large integers (although its range of valid values is much larger than long).
Another warning we might encounter due to type mismatch: "integral constant overflow".
```
long m1 = 1000000000;
long m2 = m1 * m1;                 // ok: m2 = 1000000000000000000
long m3 = 1000000000 * 1000000000; // integral constant overflow
                                   // m3 = -1486618624
```
Integer constants in MQL5 have type int, so the multiplication of million by million is performed taking into account the range of this type, which is equal to INT_MAX (2147483647). The value 1000000000000000000 causes an overflow, and m3 gets the remainder after dividing this value by the range (more on this in the sidebar below).
The fact that the receiving variable m3 has type long does not mean that the values in the expression must be converted to it beforehand. This only happens at the moment of assignment. In order for the multiplication to be performed according to the rules of long, you need to somehow specify the type long directly in the expression itself. This can be done with an explicit conversion or by using variables. In particular, obtaining the same product using a variable m1 of type long (such as m1 * m1) leads to the correct result in m2.
Signed and unsigned integers
 
Programs are not always written perfectly, with protection from all possible failures. Therefore, sometimes it happens that the integer number obtained during the calculations does not fit into the variable of the selected integer type. Then it gets the remainder of dividing this value by the maximum value (M) that can be written in the corresponding number of bytes (type size), plus 1. So for integer types with sizes from 1 to 4 bytes, M + 1 is, respectively, 256, 65536, 4294967296, and 18446744073709551616.
 
But there is a nuance for signed types. As we know, for signed numbers, the total range of values is divided approximately equally between positive and negative areas. Therefore, the new "residual" value may in 50% of cases exceed the positive or negative limit. In this case, the number turns into the "opposite": it changes sign and ends up at a distance M from the original one.
 
It is important to understand that this transformation occurs only due to a different interpretation of the bit state in the internal representation, and the state itself is the same for signed and unsigned numbers.
 
Let's explain this with an example for the smallest integer types: char and uchar.
 
Since unsigned char can store values from 0 to 255, 256 maps to 0, -1 maps to 255, 300 maps to 44, and so on. If we try to write 300 into a regular signed char, we also get 44, because 44 is in the range from 0 to 127 (the positive range of char). However, if you set the variables char and uchar to 3000, the picture will be different. The remainder of 3000 divided by 256 is 184. It ends up in uchar unchanged. However, for char, the same combination of bits results in -72. It is easy to check that 184 and -72 differ by 256.
In the following example, it is easy to spot the problem thanks to the compiler warning.
```
char c = 3000;      // truncation of constant value
Print(c);           // -72
uchar uc = 3000;    // truncation of constant value
Print(uc);          // 184
```
However, if you get an extra large number during the calculation, there will be no warning.
```
char c55 = 55;
char sm = c55 * c55;  // ok! 
Print(sm);            // 3025 -> -47
uchar um = c55 * c55; // ok!
Print(um);            // 3025 -> 209
```
A similar effect can occur when signed and unsigned integer numbers of the same size are used in the same expression since the signed operand is converted to unsigned. For example:
```
uint u = 11;
int i = -49;
Print(i + i); // -98
Print(u + i); // 4294967258 = 4294967296 - 38
```
When two negative integers add up, we get the expected result. The second expression maps the sum of -38 to the "opposite" unsigned number 4294967258.
Mixing signed and unsigned types in the same expression is not recommended because of these potential issues.
Besides that, if we subtract something from an unsigned integer, we need to make sure that the result doesn't come out negative. Otherwise, it will be converted to a positive number and can distort the idea of the algorithm, in particular, the idea of the [while](/en/book/basis/statements/statements_while)[ loop](/en/book/basis/statements/statements_while) which checks the variable for the "greater than or equal to zero" condition: since unsigned numbers are always non-negative, we can easily get an infinite loop, i.e. a program hang.
@@ -0,0 +1,46 @@
# Explicit type conversion
For explicit type conversion, MQL5 supports two forms of notation: in the C style and "functional". C-style has the following syntax:
```
target t = (target)s;
```
Where target is the name of the target type. Any expression can be a data source s. If any operations are performed in it, you must enclose the expression in parentheses so that the type conversion applies to the entire expression.
An alternative "functional" syntax looks like this:
```
target t = target(s);
```
Let's look at a couple of examples.
```
double w = 100.0, v = 7.0;
int p = (int)(w / v);      // 14
```
Here, the result of dividing two real numbers is explicitly converted to the type int. Thus, the programmer confirms their intention to discard the fractional part, and the compiler will not issue warnings. It should be noted that MQL5 has a group of functions for rounding real numbers in various ways (see [Math functions](/en/book/common/maths)).
If, on the contrary, you want to perform an operation on integer numbers with a real result, you need to apply type conversion to the operands (in the expression itself):
```
int x = 100, y = 7;
double d = (double)x / y;  // 14.28571428571429
```
Converting one of the operands is enough to automatically convert the rest to the same type.
If necessary, you can perform several type conversion operations sequentially. Because the conversion operation is right-associative, the target types will be applied in order from right to left. In the following example, we convert the quotient to type float (this conversion allows for a more compact, fewer-character representation of the value), and then to string. Without an explicit conversion to string, we would get a compiler warning "implicit number to string conversion".
```
Print("Result:" + (string)(float)(w / v)); // Result:14.28571
```
Don't use explicit type conversion just to avoid a compiler warning. If it has no practical basis, you are masking a potential error in the program.
@@ -0,0 +1,11 @@
# Statements
So far, we've learned about data types, variable declarations, and their use in expressions for calculations. However, these are only small bricks in the building with which the program can be compared. Even the simplest program consists of larger blocks that allow you to group related data processing operations and control the sequence of their execution. These blocks are called statements, and we have actually already used some of them.
In particular, the declaration of a variable (or several variables) is a statement. Assigning the expression evaluation result to a variable is also a statement. Strictly speaking, the assignment operation itself is part of the expression, so it is more correct to call such a statement a statement of expression. By the way, an expression may not contain an assignment operator (for example, if it simply calls some function that does not return a value, such as Print("Hello");).
Program execution is the progressive execution of statements: from top to bottom and from left to right (if there are several statements on one line). In the simplest case, their sequence is performed linearly, one after the other. For most programs, this is not enough, so there are various control statements. They allow you to organize loops (repeating calculations) in programs and the selection of algorithm operation options depending on the conditions.
Statements are special syntactic constructions that represent the source text written according to the rules. Statements of a particular type have their own rules, but there is something in common. Statements of all types end with a ';' except for the [compound statement](/en/book/basis/statements/statements_compound). It can do without a semicolon because its beginning and end are set by a pair of curly brackets. It is important to note that thanks to the compound statement, we can include sets of statements inside other statements, building arbitrary hierarchical structures of algorithms.
In this chapter, we will get acquainted with all types of MQL5 control statements, as well as consolidate the features of declaration and expression statements.
@@ -0,0 +1,18 @@
# Compound statements (blocks of code)
A compound statement is a generic container for other statements enclosed in curly brackets '{' and '}'. Such a block of code can be used to define the body of a function, after the header of other control statements if they require more than one controlled statement, or simply as a nested block on its own within the body of a function or other statement. This allows you to create a local, limited scope for variables. We already talked about this in the section [Context, scope, and lifetime of variables](/en/book/basis/variables/scope_and_lifetime).
In a generalized form, a compound statement can be described as follows:
```
{
[statements]
}
```
In such a schematic description, any fragment enclosed in semicircular brackets and with the superscripted opt indicates that it is optional. In this case, there may not be any nested statements inside the block.
In the following sections, we will see how compound statements are used in combination with other kinds of statements and what they can contain.
There is one nuance that is worth emphasizing: after the description of the compound statement, the semicolon ';' is not required. This distinguishes it from all other statements.
@@ -0,0 +1,173 @@
# Declaration/definition statements
The declaration of a variable, array, function, or any other named element of a program (including structures and classes, which will be discussed in Part 3) is a statement.
The declaration must contain the type and identifier of the element (see [Declaring and defining variables](/en/book/basis/variables/define_vs_declare)), as well as an optional initial value for [initialization](/en/book/basis/variables/initialization). Also, when declaring, additional modifiers can be specified that change certain characteristics of the element. In particular, we already know the [static](/en/book/basis/variables/static_variables) and [const](/en/book/basis/variables/const_variables) modifiers, and more will be added soon. Arrays require an additional specification of the dimension and number of elements (see [Description of arrays](/en/book/basis/arrays/arrays_declaration)), while functions require a list of parameters (for further details please see [Functions](/en/book/basis/functions/functions_definition)).
The variable declaration statement can be summarized as follows:
```
[modifiers] identifier type
  [= initialization expressions] ;
```
For an array, it looks like this:
```
[modifiers] identifier type [ [size_1]ᵒᵖᵗ ] [ [size_N] ]ᵒᵖᵗ(3)
  [ = { initialization_list } ]ᵒᵖᵗ ;
```
The main difference is the mandatory presence of at least one pair of square brackets (the size inside them can be indicated or not; depending on that, we get a fixed or dynamically distributed array). In total, up to 4 pairs of square brackets are allowed (4 is the maximum supported number of measurements).
In many cases, a declaration can simultaneously act as a definition, i.e. it reserves memory for the element, determines its behavior, and makes it possible to use it in the program. Specifically, the declaration of a variable or array is also a definition. From this point of view, a declaration statement can be called a definition statement all the same, but this has not become a common practice.
Our basic knowledge of functions is enough to reliably assume what their definition should look like:
```
type identifier ( [list_of_arguments] )
{
  [statements]
}
```
Type, identifier, and list of arguments make up the function header.
Please note that this is a definition since this description contains both the external attributes of the function (interface) and statements that define its internal essence (implementation). The latter is done with a block of code formed by a pair of curly brackets and immediately following the function header. As you might guess, this is an example of the compound statement we mentioned in [the previous section](/en/book/basis/statements/statements_compound). In this case, a terminological tautology is indispensable, since it is perfectly justified: the compound statement is part of the function definition statement.
A little later, we will learn why and how to separate the interface description from the implementation and thereby achieve [function declaration](/en/book/basis/functions/functions_declaration) without defining it. We will also demonstrate the difference between a [declaration and a definition using the class](/en/book/oop/classes_and_interfaces/classes_declaration_definition) as an example.
The declaration statement makes the new element available by its name in the context of the code block (see [Context, scope, and lifetime of variables](/en/book/basis/variables/scope_and_lifetime)) in which the statement is located. Recall that blocks form the local scope of objects (variables, arrays). In the first part of the book, we encountered this when describing the greeting function.
In addition to local scopes, there is always a global scope, in which you can also use declaration statements to create elements that are accessible from anywhere in the program.
If there is no static modifier in the declaration statement and it is located in some local block, then the corresponding element is created and initialized at the moment the statement is executed (strictly speaking, memory for all local variables inside the function is allocated, for the sake of efficiency, immediately upon entering the function, but they are not yet formed at that moment).
For example, the following declaration of the variable i at the beginning of the OnStart function ensures that such a variable will be created with the specified initial value (0) as soon as the function receives control (i.e., the terminal will call it because it is the main function of the script).
```
void OnStart()
{
   int i = 0;
   Print(i);
   
   // error: 'j' - undeclared identifier
   // Print(j); 
   int j = 1;
}
```
Thanks to the declaration in the first statement, the variable i is known and available in the subsequent lines of the function, in particular, in the second line with the call of the Print function, which displays the contents of the variable in the log.
The variable j described in the last line of the function will be created just before the end of the function (this, of course, is meaningless, but clear). Therefore, this variable is not known in all earlier strings of this function. An attempt to output j to the log using a commented Print call will result in an "undeclared identifier" compilation error.
Elements declared this way (inside code blocks and without the static modifier) are called automatic, because the program itself allocates memory for them when entering the block and destroys them when exiting the block (in our case, after exiting the function). Therefore, the area of memory in which this happens is called the stack ("last in, first out").
Automatic elements are created in the order in which the declaration statements are executed (first i, then j). Destruction is performed in reverse order (first j, then i).
If a variable is declared without initialization and starts to be used in subsequent statements (for example, to the right of the '=' sign) without first writing a meaningful value into it, the compiler issues a warning: "possible use of uninitialized variable".
```
void OnStart()
{
   int i, p;
   i = p; // warning: possible use of uninitialized variable 'p'
}
```
If a declaration statement has the static modifier, the corresponding element is created only once when the statement is executed for the first time, and remains in memory, regardless of exit and possible subsequent entries and exits in the same block of code. All such static members are removed only when the program is unloaded.
Despite the increased lifetime, the scope of such variables is still limited to the local context in which they are defined, and can only be accessed from later statements (located below in the code).
In contrast, declaration statements in the global context create their elements in the same order in which they appear in the source code, immediately after the program is loaded (before any standard start function is called, such as OnStart for scripts). Global objects are deleted in reverse order when the program is unloaded.
To demonstrate the aforementioned, let's create a more "cunning" example (StmtDeclaration.mq5). Recalling the skills gained in the first part, in addition to OnStart, we will write a simple function Init, which will be used in variable initialization expressions and will log a sequence of calls.
```
int Init(const int v)
{
   Print("Init: ", v);
   return v;
}
```
The Init function accepts a single parameter v of integer type int, the value of which is returned to the calling code ([return](/en/book/basis/statements/statements_return)[ statement](/en/book/basis/statements/statements_return)).
This allows using it as a wrapper to set the initial value of a variable, for example, for two global variables:
```
int k = Init(-1);
int m = Init(-2);
```
The value of the passed argument gets into the variables k and m by calling the function and returning from it. However, inside Init, we additionally output the value with Print, and thus we can track how the variables are created.
Note that we cannot use the Init function in the initialization of global variables above its definition. If we try to move the k variable declaration above the Init declaration, we get the error "'Init' is an unknown identifier". This limitation only works for the initialization of global variables, because functions are also defined globally, and the compiler builds a list of such identifiers in one go. In all other cases, the order of defining functions in the code is not important, because the compiler first registers them all in the internal list, and then mutually links their calls from blocks. In particular, you can move the entire Init function and the declaration of the global variables k and m below the OnStart function - it will not break anything.
Inside the OnStart function, we will describe several more variables using Init: local i and j, as well as static n. For simplicity, all variables are given unique values so that they can be distinguished.
```
void OnStart()
{
   Print(k);
   
   int i = Init(1);
   Print(i);
   // error: 'n' - undeclared identifier
   // Print(n);
   static int n = Init(0);
   // error: 'j' - undeclared identifier
   // Print(j);
   int j = Init(2);
   Print(j);
   Print(n);
}
```
Comments here show erroneous attempts to call the relevant variables before they are defined.
Run the script and get the following log:
```
Init: -1
Init: -2
-1
Init: 1
1
Init: 0
Init: 2
2
0
```
As we can see, the global variables were initialized before the OnStart function was called, and exactly in the order in which they were encountered in the code. Internal variables were created in the same sequence as their declaration statements were written.
If a variable is defined but not used anywhere, the compiler will issue a "variable 'name' not used" warning. This is a sign of a potential programmer error.
Looking ahead, let's say that with the help of declaration/definition statements, not only data elements (variables, arrays) or functions, but also new user-defined types (structures, classes, templates, namespaces) that are not yet known to us can be introduced into the program. Such statements can only be made at the global level, that is, outside of all functions.
It is also impossible to define a function within a function. The following code will not compile:
```
void OnStart()
{
   int Init(const int v)
   {
      Print("Init: ", v);
      return v;
   }
   int i = 0;
}
```
The compiler will generate an error: "function declarations are allowed on global, namespace, or class scope only".
@@ -0,0 +1,64 @@
# Simple statements (expressions)
Simple statements contain [expressions](/en/book/basis/expressions), such as assigning new values or calculation results to variables, as well as function calls.
Formally, the syntax looks like this:
```
expression ;
```
The semicolon at the end is important here. Since MQL5 source codes support free formatting, the ';' is the only delimiter that tells the compiler where the previous statement ended and the next one began. As a rule, statements are written on separate lines, for example, like this:
```
int i = 0, j = 1, k;   // declaration statement
++i;                   // simple statement
j += i;                // simple statement
k = (i + 1) * (j + 1); // simple statement
Print(i, " ", j);      // simple statement
```
However, the rules do not prohibit shorthand code writing:
```
int i=0,j=1;++i;j+=i;k=(i+1)*(j+1);Print(i," ",j);
```
If it weren't for the ';', adjacent expressions could silently "stick together" and lead to unintended results. For example, the expression x = y - 10 * z could well be two: x = y; and -10 * z; (-10 with a unary minus). How is this possible?
The fact is that it is syntactically permissible to write a statement that actually works in vain, i.e., does not save the result. Here is another example:
```
i + j; // warning: expression has no effect
```
The compiler issues an "expression has no effect" warning. The possibility to construct such expressions is necessary because the object types, which we will learn in [Part 3](/en/book/oop), allow for the [operator overloading](/en/book/oop/classes_and_interfaces/classes_operator_overloading), i.e., we can replace the usual meaning of operator symbols with some specific actions. Then, if the type of i and j is not int, but some class with an overridden addition operation, such a notation will have an effect, and the compiler will not issue a warning.
Simple statements can only be written inside compound statements. For example, calling the Print function outside of a function will not work:
```
Print("Hello ", Symbol());
void OnStart()
{
}
```
We will get a cascade of errors::
```
'Print' - unexpected token, probably type is missing?
'Hello, ' - declaration without type
'Hello, ' - comma expected
'Symbol' - declaration without type
'(' - comma expected
')' - semicolon expected
')' - expressions are not allowed on a global scope
```
The most relevant, in this case, is the last one: "expressions are not allowed in the global context."
@@ -0,0 +1,38 @@
# Overview of control statements
Control statements are designed to organize the non-linear execution of other statements, including declarations, expressions, and nested control statements. They can be divided into 3 types:
- repetition statements, or loops
- conditional statements for choosing one of several branches of alternative actions
- jump statements that change, if necessary, the standard behavior of the first two types of statements
Repeat and select statements consist of a header (each with a different syntax) followed by a controlled statement. If a managed part needs to specify multiple statements, it uses a compound statement. This feature is not available for jump statements. They only move the internal pointer, based on which the program determines which statement is currently to be executed, according to special rules, which we will discuss in the following sections.
In the simplest case, without control statements, the statements are executed sequentially, one after the other, as they are written in the code block (in particular, in the body of the main function OnStart for scripts). If an expression with a call to another function is encountered in a code block, the program, according to the same linear principle, begins to execute statements inside the called function, and when they are all executed, it will return to the calling code block, and execution will continue on the next statement after the function call. Control statements can significantly change this logic of work.
You can use selection inside loops or vice versa, and the nesting level is unlimited. However, too much nesting makes the program difficult to understand for the programmer. Therefore, it is recommended to allocate (transfer) code blocks into functions (one or several): inside each function, it makes sense to maintain a nesting level of no more than 2-3.
The following repetition statements are supported in MQL5:
- for loop
- while loop
- do loop
All loops allow one or more statements to be executed a given number of times or until some boolean condition is met. Executing the contents of a loop once is called an iteration. As a rule, arrays are processed in loops or periodic repeating actions are performed (usually in [scripts](/en/book/applications/script_service/scripts) or [services](/en/book/applications/script_service/services)).
Conditional statements include:
- selection with if
- selection with switch
The former allows you to specify one or more conditions, depending on the truth or falsity of which the options assigned to them (one or more statements) will be executed. The latter evaluates an expression of an integer type and selects one of several alternatives based on its value.
Finally, jump statements are:
- break
- continue
- return
Later we will consider each of them in detail.
Unlike C++, MQL5 does not have a go to statement.
@@ -0,0 +1,167 @@
# For loop
This loop is implemented by a statement with the for keyword, hence the name. In a generalized form, it can be described as follows:
```
for ( [initialization] ; [condition] ; [expression] )
  loop body
```
In the title, after the word 'for', the following is indicated in parentheses:
- Initialization: a statement for one-time initialization before the start of the loop;
- Condition: a boolean condition that is checked at the beginning of each iteration, and the loop runs as long as it is true;
- Expression: formula of calculations performed at the end of each iteration, when all statements in the loop body have been passed.
The loop body is a simple or compound statement.
All three header components are optional and may be omitted in any combination, including their absence.
Initialization may include the declaration of variables (along with setting initial values) or the assignment of values to already existing variables. Such variables are called loop variables. If they are declared in the header, then their scope and lifetime are limited to the loop.
The loop starts executing if, after initialization, the condition is true, and continues executing for as long as it is true at the beginning of each subsequent iteration. If during the next check, the condition is violated, the loop exits, i.e., control is transferred to the statement written after the loop and its body. If the condition is false before the start of the loop (after initialization), it will never be executed.
The condition and expression usually include loop variables.
Executing a loop means executing its body.
The most common form of the for loop has a single loop variable that controls the number of iterations. In the following example, we calculate the squares of the numbers in the a array.
```
int a[] = {1, 2, 3, 4, 5, 6, 7};
const int n = ArraySize(a);
for(int i = 0; i < n; ++i)
   a[i] = a[i] * a[i];
ArrayPrint(a);    // 1  4  9 16 25 36 49
// Print(i);      // error: 'i' - undeclared identifier
```
This loop is executed in the following steps:
1. A variable i with an initial value of 0 is created.
2. The condition is checked of whether the variable i is less than the size of the loop n. As long as it is true, the loop continues. If it is false, we jump to the statement calling the ArrayPrint function.
3. If the condition is true, the statements of the loop body are executed. In this case, the i-th element of the array gets the product of the initial value of this element by itself, i.e. the value of each element is replaced by its square.
4. The variable i is incremented by 1.
Then everything repeats, starting from step 2. After exiting the loop, its variable i is destroyed, and an attempt to access it will cause an error.
The expression for step 4 can be of arbitrary complexity, not just an increment of the loop variable. For example, to iterate over even or odd elements, one could write i += 2.
Regardless of how many statements make up the body of the loop, it is recommended to write it on a separate line (lines) from the header. This makes the step-by-step debugging process easier.
Initialization may include multiple variable declarations, but they must be of the same type because they are one statement. For example, to rearrange elements in reverse order, you can write such a loop (this is just a demonstration of the loop, there is a built-in function ArrayReverse to reverse the order in an array, see [Copying and editing arrays](/en/book/common/arrays/arrays_edit)):
```
for(int i = 0, j = n - 1; i < n / 2; ++i, --j)
{
   int temp = a[i];
   a[i] = a[j];
   a[j] = temp;
}
ArrayPrint(a);    // 49 36 25 16  9  4  1
```
The auxiliary variable temp is created and deleted on each pass of the loop, but the compiler allocates memory for it only once, as for all local variables, when entering the function. This optimization works well for built-in types. However, if [a custom class object](/en/book/oop) is described in the loop, then its constructor and destructor will be called at each iteration.
It is acceptable to change the loop variable in the loop body, but this technique is only used in very exotic cases. It is not recommended to do this, as this may cause errors (in particular, processed elements can be skipped or execution can get into an infinite loop).
To demonstrate the ability to omit header components, let's imagine the following problem: We need to find the number of elements of the same array the sum of which is less than 100. To do this, we need a counter variable k defined before the loop because it must continue to exist after its completion. We will also create the sum variable to calculate the sum on a cumulative basis.
```
int k = 0, sum = 0;
for( ; sum < 100; )
{
  sum += a[k++];
}
 
Print(k - 1, " ", sum - a[k - 1]); // 2 85
```
Thus, there is no need to do initialization in the header. In addition, the k counter is incremented using a postfix increment directly in the expression that calculates the sum (when accessing an array element). Therefore, we do not need an expression in the title.
At the end of the loop, we print out k and the sum minus the last added element, because it was the one that exceeded our limit of 100.
Note that we are using a compound block even though there is only one statement in the loop body. This is useful because when the program grows, everything is already done for adding additional statements inside the brackets. In addition, this approach guarantees a uniform style for all loops. But the choice, in any case, is up to the programmer.
In the explicit, maximally abbreviated version, the cycle header might look like this:
```
for( ; ; )
{
   // ...       // periodic actions
   Sleep(1000); // pause the program for 1 second
}
```
If there are no statements in the body of such a loop that would interrupt the loop due to some conditions, it will be executed indefinitely. We'll learn how to break and test conditions in [Break ](/en/book/basis/statements/statements_break)[jump](/en/book/basis/statements/statements_break) and [If ](/en/book/basis/statements/statements_if)[selection](/en/book/basis/statements/statements_if) respectively.
Such looping algorithms are usually used in services (they are designed for constant background work) to monitor the state of the terminal or external network resources. They usually contain statements that pause the program at a specified interval, for example, using the built-in function [Sleep](/en/book/common/timing/timing_sleep). Without this precaution, an infinite loop will load 100% of one processor core.
Script StmtLoopsFor.mq5 contains an infinite loop at the end, but it is for demonstration purposes only.
```
for( ; ; )
{
   Comment(GetTickCount());
   Sleep(1000); // 1000 ms
  
   // the loop can be exited only by deleting the script at the user's command
   // after 3 seconds of waiting we will get the message 'Abnormal termination'
}
Comment("");  // this line will never be executed
```
In the loop, once per second, the computer's internal timer ([GetTickCount](/en/book/common/timing/timing_count)) is displayed using the [Comment](/en/book/common/output/output_comment) function: the value is displayed in the upper left corner of the chart. Only the user can interrupt the loop by deleting the entire script from the chart (the "Delete" button in the Experts dialog). This code does not check for such user requests to stop inside the loop, although there is a built-in function [IsStopped](/en/book/common/environment/env_stop) for this purpose. It returns true if the user has given the command to stop. In the program, especially if there are loops and long-term calculations, it is desirable to provide for checking the value of this function and voluntarily terminate the loop and the entire program upon receipt of true. Otherwise, the terminal will forcibly terminate the script after 3 seconds of waiting (with output to the "Abnormal termination" log), which will happen in this example.
A better version of this loop should be:
```
for( ; !IsStopped(); ) // continue until user interrupt
{
   Comment(GetTickCount());
   Sleep(1000); // 1000 ms
}
Comment("");    // will clear the comment
```
However, this loop would be better implemented using another repeat statement [while](/en/book/basis/statements/statements_while). As a rule of thumb, a for loop should only be used when there is an obvious loop variable and/or a predetermined number of iterations. In this case, these conditions are not met.
Loop variables are usually integers, although other types are allowed, such as double. This is due to the fact that the very logic of the loop operation implies the numbering of iterations. In addition, it is always possible to calculate the necessary real numbers from an integer index, and with greater accuracy. For example, the following loop iterates over values from 0.0 to 1.0 in increments of 0.01:
```
for(double x = 0.0; x < 1.0; x += 0.01) { ... }
```
It can be replaced by a similar loop with an integer variable:
```
for(int i = 0; i < 100; ++i) { double x = i * 0.01; ... }
```
In the first case, when adding x += 0.01, the error of floating-point calculations gradually accumulates. In the second case, each value x is obtained in one operation i * 0.01, with the maximum available precision.
It is customary to give loop variables the following single-letter names, for example, i, j, k, m, p, q. Multiple names are required when loops are nested or both forward (increasing) and backward (decreasing) indexes are calculated within the same loop.
By the way, here is an example of a nested loop. The following code calculates and stores the multiplication table in a two-dimensional array.
```
int table[10][10] = {0};
for(int i = 1; i <= 10; ++i)
{
   for(int j = 1; j <= 10; ++j)
   {
      table[i - 1][j - 1] = i * j;
   }
}
ArrayPrint(table);
```
@@ -0,0 +1,76 @@
# While loop
This loop is described using the while keyword. It repeats the execution of controlled statements as long as the logical expression in its header is true.
```
while ( condition )
  loop body
```
The condition is an arbitrary expression of a boolean type. The presence of the condition is mandatory. If the condition is false before the start of the loop, the loop will never execute.
Unlike C++, MQL5 does not support defining variables in the while loop header.
Variables included in the condition must be defined before the loop.
The loop body is a simple or compound statement.
The while loop is usually used when the number of iterations is not defined. So, an example with the loop that outputs a computer timer counter every second can be written using a while loop and checking the stop flag (by calling the IsStopped function) as follows (StmtLoopsWhile.mq5):
```
while(!IsStopped())
{
   Comment(GetTickCount());
   Sleep(1000);
}
Comment("");
```
Also, the while loop is convenient when the loop termination condition can be combined with the modification of variables in one expression. The next loop is executed until the variable i reaches zero (0 is treated as false).
```
int i = 5;
while(--i) // warning: expression not boolean
{
   Print(i);
}
```
However, in this case, the header expression is not boolean (and is implicitly converted to false or true). The compiler generates the relevant warning. It is desirable to always compose expressions taking into account the expected (according to the rules) characteristics. Below is the correct loop version:
```
int i = 5;
while(--i > 0)
{
   Print(i);
}
```
The loop can also be used with a simple statement (no block):
```
while(i < 10)
   Print(++i);
```
Note that a simple statement ends with a semicolon. It also demonstrates that changing the variable being checked in the header is done inside the loop.
When working with loops, be careful when using unsigned integers. For example, the next loop will never end, because its condition is always true (in theory, the compiler could issue warnings in such places, but it does not). After zero, the counter will "turn" into a large positive number (UINT_MAX) and the loop will continue.
```
uint i = 5;
while(--i >= 0)
{
   Print(i);
}
```
From the user's point of view, the MQL program will freeze (stop responding to commands), although it will still consume resources (processor and memory).
while loops can be nested like other kinds of repetition statements.
@@ -0,0 +1,33 @@
# Do loop
This loop is similar to the while loop, but its condition is checked after the loop body. Due to this, controlled statements must be executed at least once.
Two keywords, do and while, are used to describe the loop:
```
do
  loop body
while ( condition ) ;
```
Thus, the loop header is separated, and after the logical condition in brackets, there should be a semicolon. The condition cannot be omitted. When it becomes false, the loop exits.
Variables included in the condition must be defined before the loop.
The loop body is a simple or compound statement.
The following example calculates a sequence of numbers starting from 1, in which each next number is obtained by multiplying the previous one by the square root of two, the predefined constant M_SQRT2 (StmtLoopsDo.mq5).
```
double d = 1.0;
do
{
   Print(d);
   d *= M_SQRT2;
}
while(d < 100.0);
```
The process terminates when the number exceeds 100.
@@ -0,0 +1,141 @@
# If selection
The if statement has several forms. In its simplest case, it executes the dependent statement if the specified condition is true:
```
if ( condition )
  statement
```
If the condition is false, the statement is skipped and the execution immediately jumps to the rest of the algorithm (subsequent statements, if any).
The statement can be simple or compound. A condition is an expression of a boolean or castable type.
The second form allows you to specify two branches of actions: not only for the true condition (statement_A) but also for the false (statement_B):
```
if ( condition )
  statement_A
else
  statement_B
```
Whichever of the controlled statements is executed, the algorithm will then continue following the statements below the if/else statement.
For example, a script can follow a different strategy depending on the timeframe of the chart it is placed on. For this purpose, it is enough to analyze the value returned by the [Period](/en/book/applications/charts/charts_main_properties) built-in function. The value is of the [ENUM_TIMEFRAMES](/en/book/applications/timeseries/timeseries_symbol_period) enum type. If it is less than PERIOD_D1, it means short-term trading, otherwise, long-term trading (StmtSelectionIf.mq5).
```
if(Period() < PERIOD_D1)
{
   Print("Intraday");
}
else
{
   Print("Interday");
}
```
As a statement in the else branch, it is allowed to specify the following operator if, and thus arrange them into a chain of successive checks. For example, the following fragment counts the number of capital letters and punctuation symbols (more precisely, non-Latin letters) in a string.
```
string s = "Hello, " + Symbol();
int capital = 0, punctuation = 0;
for(int i = 0; i < StringLen(s); ++i)
{
   if(s[i] >= 'A' && s[i] <= 'Z')
      ++capital;
   else if(!(s[i] >= 'a' && s[i] <= 'z'))
      ++punctuation;
      
}
Print(capital, " ", punctuation);
```
The loop is organized through all the characters of the string (numbering starts from 0) and the [StringLen](/en/book/common/strings/strings_init) function returns the length of the string. The first if checks each character to see if it belongs to the range 'A' to 'Z' and, if successful, increments the capital counter by 1. If the character does not fall into this range, the second if is run, in which the condition for belonging to the range of lowercase letters (s[i] >= 'a' && s[i] <= 'z') is inverted with '!'. In other words, the condition means that the character is not in the given range. Given two consecutive checks, if the character is not an uppercase letter (else) and not a lowercase letter (the second if), we can conclude that the character is not a letter of the Latin alphabet. In this case, we increment the punctuation counter.
The same checks could be written in a more detailed form, with '{...}' blocks for clarity.
```
int capital = 0, small = 0, punctuation = 0;
for(int i = 0; i < StringLen(s); ++i)
{
   if(s[i] >= 'A' && s[i] <= 'Z')
   {
      ++capital;
   }
   else
   {
      if(s[i] >= 'a' && s[i] <= 'z')
      {
         ++small;
      }
      else
      {
         ++punctuation;
      }
   }
}
```
The use of curly brackets helps to avoid logical errors associated which can occur when the programmer is only guided by indentation in the code. In particular, the most common problem is called the "hanging" else.
When if statements are nested, sometimes there are fewer else branches than if. Here is one example:
```
factor = 0.0;
if(mode > 10)
   if(mode > 20)
      factor = +1.0;
else
   factor = -1.0;
```
The indentation indicates what kind of logic the programmer meant: factor should become +1 when mode is greater than 20, remain equal to 0 when mode is between 10 and 20, and change to -1 otherwise (mode <= 10). But will the code work that way?
In MQL5, each else is assumed to refer to the nearest previous if (which does not have a else). As a result, the compiler will treat the statements as follows:
```
factor = 0.0;
if(mode > 10)
   if(mode > 20)
      factor = +1.0;
   else
      factor = -1.0;
```
So the factor will be -1 in the mode range from 10 to 20, and 0 for mode <= 10. The most interesting thing is that the program does not produce any formal errors, neither during compilation nor during execution. And yet it doesn't work correctly.
To eliminate such subtle logical problems allows the placement of curly brackets.
```
if(mode > 10)
{
   if(mode > 20)
      factor = +1.0;
}
else
   factor = -1.0;
```
To keep the design consistent, it is desirable to use blocks in all branches of the statement if at least one block has already been required in it.
When using the loop to check equality, take into account the possibility of a typo when one '=' is written instead of two characters '=='. This turns the comparison into an assignment, and the assigned value is analyzed as a logical condition. For example,
```
// should have been x == y + 1, which would give false and skip the if
if(x = y + 1) // warning: expression not boolean
{
   // assigned x = 5 and treated x as true, so if is executed
}
```
The compiler will produce a warning "expression not boolean".
@@ -0,0 +1,117 @@
# Switch selection
The switch operator provides the ability to choose one of several algorithm options. As a rule, the number of options is significantly higher than two, because otherwise, it is easier to use the if/else statement. In theory, the chain of if/else statements allows having an equivalent of switch in many cases (but not all). An important feature of switch is that all options are selected (identified) based on the integer expression value, usually a variable.
In general case, the switch statement looks as follows:
```
switch ( expression )
{
   case constant-expression : statements [break; ]
   ...
   [ default : statements ] 
}
```
The statement header starts with the keyword switch. It must be followed by an expression in parentheses. The block with curly brackets is also required.
Integer values that can be obtained by evaluating an expression should be specified as constants after the case keyword. A constant is a literal of any [integer types](/en/book/basis/builtin_types/integer_numbers), for example, int (10, 123), ushort (characters 'A', 's', '*' etc.), or [enum](/en/book/basis/builtin_types/enums) elements. Real numbers, variables, or expressions are not allowed here.
There may be many such case options, or may not be at all, which is indicated by semicircular brackets with index opt(n). All variants must have unique constants (no repetitions).
For each alternative declared with case, a statement must be written after the colon, which will be executed if the value of the expression is equal to the corresponding constant. Again, a statement can be simple or compound. In addition, it is permissible to write several simple statements without enclosing them in curly brackets: they will still be executed as a group (a compound statement).
One or more of these statements can be followed by the [break](/en/book/basis/statements/statements_break) jump statement.
If there is a break, after executing the previous statements from the case branch, the switch statement exits, i.e., control is transferred to the statements below switch.
In the absence of break, the statements of the next branch or several branches case continue to be executed, that is, until the first encountered break or the end of the block switch. This is called "fall-through".
Thus, the switch statement not only allows splitting the algorithm execution flow into several alternatives but also combining them, which is not available for the if operator. On the other hand, in the switch statement, unlike if, you cannot select a range of values as a condition for activating alternatives.
The default keyword allows you to set the default algorithm variant, that is, for any other expression values except for constants from all cases. The default option may not be present, or there must be only one.
The sequence in which case constants and default are listed can be arbitrary.
Even if there is no algorithm for the default branch yet, it is recommended to make it explicitly empty, i.e. containing break. An empty default will remind you and other programmers that other options exist but are considered unimportant because otherwise, the default branch would have to signal an error.
Several case variants with different constants can be listed one below the other (or left to right) without statements, but the last one must have a statement. Such combined cases are indicated on the diagram by the index (i).
Here is the simplest and most useless switch:
```
switch(0)
{
}
```
Let's consider a more complex example with different modes (StmtSelectionSwitch.mq5). In it, the switch operator is placed inside the loop to show how its work depends on the values of the control variable i.
```
for(int i = 0; i < 7; i++)
{
   double factor = 1.0;
   
   switch(i)
   {
      case -1:
         Print("-1: Never hit");
         break;
      case 1:
         Print("Case 1");
         factor = 1.5;
         break;
      case 2: // fall-through, no break (!)
         Print("Case 2");
         factor *= 2;
      case 3: // same statements for 3 and 4
      case 4:
         Print("Case 3 & 4");
         {
            double local_var = i * i;
            factor *= local_var;
         }
         break;
      case 5:
         Print("Case 5");
         factor = 100;
         break;
      default:
         Print("Default: ", i);
   }
   
   Print(factor);
}
```
The -1 option will fail because the loop changes the variable i from 0 to 6 (inclusive). When i is 0, the default branch will trigger. It will also take control when i is equal to 6. All other possible i values are distributed according to the corresponding case directives. At the same time, there is no break statement after case 2, and therefore the code for options 3 and 4 will be executed in addition to 2 (in such cases, it is always recommended to leave a comment that this was done intentionally).
Cases 3 and 4 have a common statement block. But it is also important to note here that if you want to declare a local variable inside one of the case options, you need to enclose the statements in a nested compound block ('{...}'). Here, the variable local_varis defined this way.
It is worth advising that in the default case, there is no break statement. It's redundant because default is written last in this case. However, many programmers advise inserting break at the end of any option, even the last one, because it can cease to be the last in the process of subsequent modifications of the code, and then it is easy to forget to add break, which will probably lead to an error in the program logic.
If in switch there is no default, and the header expression does not match any of the case constants, the entire switch is skipped.
As a result of the script execution, we will receive the following messages in the log:
```
Default: 0
1.0
Case 1
1.5
Case 2
Case 3 & 4
8.0
Case 3 & 4
9.0
Case 3 & 4
16.0
Case 5
100.0
Default: 6
1.0
```
@@ -0,0 +1,107 @@
# Break jump
The break operator is intended for early termination of the for, while, do loops, as well as exit from the switch selection statement. The operator can only be applied within the specified statements and only affects the one immediately containing break if there are multiple nested ones. After processing the break statement, program execution continues to the statement following the interrupted loop or switch.
The syntax is very simple: the keyword break and a semicolon:
```
break ;
```
When used inside loops, break is usually implemented in one of the branches of the [if/else](/en/book/basis/statements/statements_if) conditional operator.
Consider a script that prints the current system time counter once per second, but no more than 100 times. It provides for handling the interruption of the process by the user: for this, the function IsStopped is polled in the conditional operator if and its dependent statement contains break (StmtJumpBreak.mq5).
```
int count = 0;
while(++count < 100)
{
   Comment(GetTickCount());
   Sleep(1000);
   if(IsStopped())
   {
      Print("Terminated by user");
      break;
   }
}
```
In the following example, a diagonal matrix is filled in with a times table (the top right corner will remain filled with zeros).
```
int a[10][10] = {0};
for(int i = 0; i < 10; ++i)
{
   for(int j = 0; j < 10; ++j)
   {
      if(j > i)
         break;
      a[i][j] = (i + 1) * (j + 1);
   }
}
ArrayPrint(a);
```
When the inner loop variable j is greater than the outer loop variable i, the break statement breaks the inner loop. Of course, this is not the best way to fill the matrix diagonally: it would be easier to loop over j from 0 to i without any break, but here it demonstrates the presence of equivalent constructions with break and without break.
Although things may not be so obvious in production projects, it is recommended to avoid the break operator whenever possible and replace it with additional variables (for example, a boolean variable with a "telling" name needAbreak), which should be used in terminal expressions in loop headers to break them in the standard way.
Imagine that two nested loops are used to find duplicate characters in a string. The first loop sequentially makes each character of the string current and the second runs through the remaining (to the right) characters.
```
string s = "Hello, " + Symbol();
ushort d = 0;
const int n = StringLen(s);
for(int i = 0; i < n; ++i)
{
   for(int j = i + 1; j < n; ++j)
   {
      if(s[i] == s[j])
      {
         d = s[i];
         break;
      }
   }
}
```
If the characters at positions i and j match, remember the duplicate character and exit the loop via break.
It could be assumed that the variable d should contain the letter 'l' after the execution of this fragment. However, if you place the script on the most popular instrument "EURUSD", the answer will be 'U'. The thing is that break breaks only the inner loop, and after finding the first duplicate ('ll' in the word "Hello"), the loop continues on i. Therefore, to exit from several nested loops at once, additional measures must be taken.
The most popular way is to include in the condition of the outer loop (or all outer loops) a variable that is filled in the inner loop. In our case, there is already such a variable: d.
```
for(int i = 0; i < n && d == 0; ++i)
{
   for(int j = i + 1; j < n; ++j)
   {
      if(s[i] == s[j])
      {
         d = s[i];
         break;
      }
   }
}
```
Checking d for being equal to 0 will now stop the outer loop after finding the first duplicate. But the same check can be added to the inner loop, which eliminates the need to use break.
```
for(int i = 0; i < n && d == 0; ++i)
{
   for(int j = i + 1; j < n && d == 0; ++j)
   {
      if(s[i] == s[j])
      {
         d = s[i];
      }
   }
}
```
@@ -0,0 +1,58 @@
# Continue jump
The continue statement breaks the current iteration of the innermost loop containing continue and initiates the next iteration. The statement can only be used inside for, while and do loops. Execution of continue inside for results in the next calculation of the expression in the loop header (increment/decrement of the loop variable), after which the loop continuation condition is checked. Executing continue inside while or do immediately results in checking the condition in the loop header.
The statement consists of the keyword continue and a semicolon:
```
continue ;
```
It is usually placed in one of the branches of the [if/else](/en/book/basis/statements/statements_if) or [switch](/en/book/basis/statements/statements_switch) conditional statement.
For example, we can generate a times table with gaps: when the product of two indexes is odd, the corresponding array element will remain zero (StmtJumpContinue.mq5).
```
int a[10][10] = {0};
for(int i = 0; i < 10; ++i)
{
   for(int j = 0; j < 10; ++j)
   {
      if((j * i) % 2 == 1)
         continue;
      a[i][j] = (i + 1) * (j + 1);
   }
}
ArrayPrint(a);
```
And here's how you can calculate the sum of the positive elements of an array.
```
int b[10] = {1, -2, 3, 4, -5, -6, 7, 8, -9, 10};
int sum = 0;
for(int i = 0; i < 10; ++i)
{
   if(b[i] < 0) continue;
   sum += b[i];
}
Print(sum); // 33
```
Note that the same loop can be rewritten without continue but with a greater nesting of code blocks:
```
for(int i = 0; i < 10; ++i)
{
   if(b[i] >= 0)
   {
      sum += b[i];
   }
}
```
Thus, operator continue is often used to simplify code formatting (especially if there are several conditions to pass). However, which of the two approaches to choose is a matter of personal preference.
@@ -0,0 +1,47 @@
# Return jump
The return operator is designed to return control from [functions](/en/book/basis/functions). Given that all executable statements are inside a particular function, it can be indirectly used to interrupt containing it loops for, while, and do of any nesting level. It should be taken into account that unlike continue and, especially, break, all statements following interrupted loops inside the function will also be ignored.
The syntax for the return operator:
```
return ([expression]) ;
```
The need to specify an expression is determined by the function signature (more on this will be discussed in the [relevant section](/en/book/basis/functions/functions_return)). For a general understanding of how return works in the context of control statements, let's view an example with the main script function OnStart. Since it is of type void, i.e. it does not return anything, the operator takes the following form:
```
return ;
```
In the section on [break](/en/book/basis/statements/statements_break), we implemented an algorithm for finding duplicate characters in a string. To break two nested loops, we not only use break but also modify the condition of the outer loop.
With the return operator, this can be done in a simpler way (StmtJumpReturn.mq5).
```
void OnStart()
{
   string s = "Hello, " + Symbol();
   const int n = StringLen(s);
   for(int i = 0; i < n; ++i)
   {
      for(int j = i + 1; j < n; ++j)
      {
         if(s[i] == s[j])
         {
            PrintFormat("Duplicate: %c", s[i]);
            return;
         }
      }
   }
   
   Print("No duplicates");
}
```
If equality is found in the if operator, we display the symbol and exit the function. If this algorithm was in a custom function other than OnStart, we could define a return type for it (for example, ushort instead of void) and pass the found character using the full form return to the calling code.
Since the double letter 'l' is known to exist in the test string, the statement after the loops (Print) will not be executed.
@@ -0,0 +1,35 @@
# Empty statement
The empty statement is the simplest in the language. It consists of only one character, the semicolon ';'
An empty statement is used in the program in those places where the syntax requires the presence of a statement, but the logic of the algorithm instructs to do nothing.
For example, the following while loop is used to find a space in a string. The whole essence of the algorithm is performed directly in the loop header, so its body must be empty. We could write an empty block of curly brackets, but an empty statement would also work here. (StmtNull.mq5).
```
int i = 0;
ushort c;
string s = "Hello, " + Symbol();
while((c = s[i++]) != ' ' && c != 0); // intentional ';' (!)
if(c == ' ')
{
   Print("Space found at: ", i);
}
```
Note that if the semicolon at the end of the while header is omitted (perhaps by accident), then the if statement will be treated as the body of the loop. As a result, there will be no output to the log by the Print function. In fact, the program will not work correctly, although without noticeable errors.
The opposite situation is also possible: an extra semicolon after the loop header (where it should not have been) will "detach" the loop body from the header, i.e. only an empty statement will be executed in the loop.
In this regard, optional semicolons should be checked in the code, and wherever they are placed intentionally, leave a comment with explanations.
By the way, from a formal point of view, the empty statement is also used in the [for](/en/book/basis/statements/statements_for) statement when we omit the initialization expression. In fact, there is always initialization:
```
for ( [initialization] ; [end loop condition]; [post-expression] )
  loop body
```
The first character ';' is part of an initialization statement, which can be an expression or an empty statement: both contain the character ';' at the end, with the latter containing nothing but ';'. Thus, optionality (emptiness) is achieved.
@@ -0,0 +1,11 @@
# Functions
A function is a named block with statements. Almost the entire application algorithm of the program is contained in functions. Outside of functions, only auxiliary operations are performed, such as creating and deleting global variables.
The execution of statements within a function occurs when we call that function. Some functions, the main ones, are called automatically by the terminal when various events occur. They are also referred to as the MQL program entry points or event handlers. In particular, we already know that when we run a script on a chart, the terminal calls its main function OnStart. In other types of programs, there are other functions called by the terminal, which we will discuss in detail in the [fifth](/en/book/applications) and [sixth](/en/book/automation) chapters covering the trading architecture of the MQL5 API.
In this chapter, we will learn how to define and declare a function, how to describe and pass parameters to it, and how to return the result of its work from the function.
We will also talk about function overloading, i.e., the ability to provide multiple functions with the same name, and how this can be useful.
Finally, we will get acquainted with a new type: a pointer to a function.
@@ -0,0 +1,65 @@
# Function definition
A function definition consists of the value type it returns, an identifier, a list of parameters in parentheses, and a body — a block of code with statements. Parameters in the list are separated by commas. Each parameter is given a type, a name, and optionally a default value.
```
result_type function_identifier ( [parameter_type parameter_identifier
                                       = value_by_default] ,... )
{
  [statement]
   ...
}
```
It is allowed to create functions without parameters: then there is no list, and empty brackets are placed after the function name (they cannot be omitted). Optionally, you can write the void keyword between the brackets to emphasize that there are no parameters. For example, like this:
```
void OnStart(void)
{
}
```
The combination of return type, number and types of parameters in the list is called a function prototype or signature. Different functions can have the same prototype.
In previous sections, we have already seen function definitions such as OnStart and Greeting. Now let's try to implement the calculation of Fibonacci numbers as a test function. These numbers are calculated by the following formula:
```
f[0] = 1
f[1] = 1
f[i] = f[i - 1] + f[i - 2], i > 1
```
The first two numbers are 1, and all subsequent numbers are the sum of the previous two. We give the beginning of the series: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55...
You can calculate the number at a given index using the following function (FuncFibo.mq5).
```
int Fibo(const int n)
{
   int prev = 0;
   int result = 1;
   for(int i = 0; i < n; ++i)
   {
      int temp = result;
      result = result + prev;
      prev = temp;
   }
   return result;
}
```
It takes one parameter n of type int and returns a result of type int. The n parameter has the const modifier because we are not going to change n inside the function (such an explicit declaration of restrictions on the "rights" of variables is welcome because it helps avoid random errors).
Local variables prev and result will store the current values of the last two numbers in the series. In the loop over i we calculate their sum, getting the next number of the sequence. Previously, the old value result is written to the variable temp, so that after summation, it is transferred to prev.
After executing the loop a given number of times, the result variable contains the desired number. We return it from the function using the result statement.
The input parameter of a function is also a local variable that will be initialized to the actual value during the function call. This value is passed "outside" from the statement with the function call.
Parameter names must be unique and must not match local variable names.
The body of a function is a block of code that defines the [scope and lifetime of local variables](/en/book/basis/variables/scope_and_lifetime). Their definition and operation principles were discussed in the sections [Declaration/definition statements](/en/book/basis/statements/statements_declaration) and [Initialization](/en/book/basis/variables/initialization).
@@ -0,0 +1,29 @@
# Function call
A function is called when its name is mentioned in an expression. After the name, there should be a pair of parentheses, in which the arguments corresponding to the function parameters (if there is a list of parameters in its definition) are indicated, separated by commas.
A little later, we will look at the [function pointer](/en/book/basis/functions/functions_typedef) type, which allows you to create variables that point to a function with specific characteristics, and then call it not by name, but through this variable.
Continuing the example with the Fibo function, let's call it from the OnStart function. To do this, let's create a variable f to store the resulting number and in its initialization expression we indicate the name of the function Fibo and an integer (for example, 10) as an argument, in parentheses.
```
void OnStart()
{
   int f = Fibo(10); 
   Print(f); // 89
}
```
We are not required to create a variable to receive a value from a function. Instead, you can call the function directly from an expression, such as "2*Fibo(10)" or "Print(Fibo(10))". Then its value will be substituted into the expression at the place of the call. Here, the auxiliary variable f is introduced to implement the call and return of a value in a separate statement.
The call process includes the following steps:
- Execution of the statement sequence of the calling function (OnStart) is suspended;
- The value of the argument gets into the input parameter n of the called function (Fibo);
- The execution of its statements starts;
- When it is completely finished, it sends the result back (remember the return statement inside);
- The result is written to the variable f; and
- After that, the execution of the OnStart function continues, that is, the number is printed to the log (Print).
For each function call, the compiler generates auxiliary binary code (the programmer does not need to worry about it). The idea of this code is that before calling the function, it pushes the current position in the program onto the stack, and after the call is completed, it retrieves it and uses it to return to the statements following the function call. When one function calls another, that one calls one more function, the second calls a third, and so on, the return addresses of transitions throughout the hierarchy of called functions are accumulated on the stack (hence the name stack). As nested function calls are processed, the stack will be cleared in reverse order. Note that the stack also allocates memory for the local variables of each function.

Some files were not shown because too many files have changed in this diff Show More