1. Главная страница » Компьютеры

Array out of range mql4

Автор: | 16.12.2019

Does somebody know how to avoid the error array out of range when trying to display long number of bars (lets say 7000) in an indicator buffer?

2 Answers 2

While the arrays (problem) look similar, there is a major difference

MQL4 Indicators use "other" mechanics for handling arrays than "ordinary" arrays have.

Yes, MT4 throws Fatal Error on an attempt to handle a wrong ptr -> array[aStaticSIZE] and one has to take due care to either avoid such case, or trim ptr ( alike in low-latency circular-buffer scenarios ) not to direct past the array boundary ( underflow / overflow ) or extend the array[] via ArrayResize() so as to keep pace with the ptr growth ( until memory allows ) ) on arrays declared as dynamic double Array[]; , however MQL4 Technical Indicators have completely other situation.

Limits? Yes. . . . . . ( And one ought to review on each New-MQL4.56789 stealth update )

As of a "New"-MQL4.56789-Build-840 , your "ordinary" array cannot have more than 2.147.483.647 elements, if O/S memory-pool manager allows, so you ought to have plenty of space, even if using higher dimensionality mappings < 2D | 3D | 4D >.

Years ago ‘ve used many parallel 2D / 3D-arrays for fast and private ( safely encapsulated ) heap / stack-handlers for maintaining highly dynamic entities in scales of 100k+ rows / 2D-planes and all worked well-oiled in old- MQL4 .

Читайте также:  Micromax canvas 5 обзор

So some 7k+ elements should not make you any worries.

Indicators use array indirectly, via an automated Buffer-handler

In this sense, your code need not care about these issues.

Начиная с билда 600 язык программирования MQL4 полностью переработан и достиг уровня MQL5 — теперь писать торговых роботов на MQL4/MQL5 можно в единой среде разработке MetaEditor и используя единые стиль, библиотеки и инструменты отладки.

MQL4 пользуется заслуженной популярностью среди разработчиков автоматических торговых систем благодаря простоте изучения и огромному количеству кода, наработанного за многие годы использования терминала MetaTrader 4. Но вместе с тем, ему были присущи и некоторые недостатки, проистекающие из главного преимущества — простой язык программирования не позволял писать сложные системы и затруднял портирование отлаженных библиотек из языков высокого уровня. Поэтому мы решили перенести в него максимум функций и возможностей языка MQL5, полностью сохраняя функционал MQL4. То есть все мощные функции MQL5, включая ООП и компилятор в нативный код, станут доступны в MQL4.

Для этого мы разработали единый компилятор, который автоматически поддерживает оба языка MQL4 и MQL5. MetaEditor тоже становится единым для обеих платформ MetaTrader 4 и MetaTrader 5. С его помощью можно компилировать как MQL4, так и MQL5 из любой версии. Для работы также сразу становится доступным и MQL5 Storage.

Защита MQL4-программ поднимается на один уровень с MQL5, в новых EX4/EX5-файлах очень серьезная и полностью переработанная защита по сравнению со старым EX4. А это значит, что для MetaTrader 4 становится доступным и Маркет защищенных приложений на EX4.

Кроме того, в язык MQL4 добавлены новые графические объекты и новые функции по работе с графиками. Стандартная библиотека из MQL5 будет портирована и в MQL4, что предоставит для разработчиков программ новые возможности по созданию собственных графических интерфейсов и торговых библиотек. Теперь в MetaTrader 4 можно создавать полноценные приложения с использованием ресурсов.

Добавлены новые типы данных char, short, long, uchar, ushort, uint, ulong и double. Это позволит легко переносить коды из других C++ подобных языков. Данные разного типа обрабатываются с разной скоростью. Целочисленные данные обрабатываются быстрее всего. Для обработки данных двойной точности используется специальный сопроцессор. Однако из-за сложности внутреннего представления данных с плавающей точкой они обрабатывается дольше, чем целочисленные. Введена операция приведения типов.

Строки теперь представлены в формате Unicode, раньше они были в формате ANSI (однобайтовые). Это необходимо учитывать, если программа использует DLL и передает в них строковые переменные.

Предопределенная переменная Volume теперь имеет тип long, таймсерии для доступа к объемам также представлены массивами типа long. В старых программах MQL4 рекомендуется делать явное приведение данных этого типа к целевому типу, чтобы избежать ошибки переполнения типа.

Добавлены структуры и классы, указатели объектов, тип void и ключевое слово this для получения объектом ссылки на самого себя. Поддерживаются все стандарты объектно-ориентированного программирования:

ООП позволяет писать программы с использованием классов, что облегчает отладку и разработку больших программ, позволяет многократно использовать наработки благодаря наследованию. Но это не означает, что теперь нельзя писать на MQL4 в процедурном стиле, как это было раньше. Вы можете писать свои программы как и раньше, если вам не нужны новые возможности.

Предопределенные функции init(), deinit() и start() остались для совместимости, но вместо них теперь можно использовать OnInit(), OnDeinit(), OnStart(), OnCalculate(), OnTick(). Кроме того, добавились новые предопределенные функции-обработчики OnTimer(), OnChartEvent(), OnTester(). В старом MQL4 предопределенные функции могли иметь любые параметры и любой тип возврата, вызов этих функций осуществлялся не по сигнатуре, а по имени. В новом MQL4 все предопределенные функции должны точно соответствовать своим сигнатурам, то есть иметь четко определенный состав параметров и тип возврата.

Имена переменных теперь не могут содержать спецсимволы и точки, а также именами не могут быть новые ключевые слова языка MQL4. Старые программы на MQL4 можно будет прогнать через новый компилятор и легко исправить все такие ошибки по сообщениям компилятора.

Приоритет операций теперь приведен к стандартам языка Cи. В старых программах MQL4 для надежности вы можете расставить круглые скобки для явного указания приоритета, если не уверены.

В логических операциях теперь используется укороченная проверка условия. В старой версии MQL4 вычислялись все выражения, и только потом производилась проверка условия. Например, пусть есть проверка двух условий с использованием логического И:

if (condition1 && condition2)
<
// некоторый блок операций
>

Если выражение condition1 окажется равным false, то вычисление выражения condition2 проводиться уже не будет, так как результат false && true все равно будет равен false.

Изменилось поведение функции ArrayCopyRates() — в старой версии MQL4 эта функция производила копирование ценовых серий в массив double[][6]. Теперь для получения таймсерий необходимо использовать массив из элементов структуры MqlRates:

//— Структура для хранения информации о ценах, объемах и спреде.
struct MqlRates
<
datetime time ; // время начала периода
double open ; // цена открытия
double high ; // наивысшая цена за период
double low ; // наименьшая цена за период
double close ; // цена закрытия
long tick_volume ; // тиковый объем
int spread ; // спред
long real_volume ; // биржевой объем
>;

Новый формат функции также производит виртуальное копирование, то есть реального копирования не происходит, а при обращении к скопированным значениям происходит обращения прямо к ценовым данным.

Для совместимости со старыми MQL4 программами сохранился и старый формат вызова, но теперь производится реальное копирование данных в массив типа double.

Это означает, что при изменении значений в таймсерии (добавлении новых баров, перестройке, обновлении цены Close последнего бара), необходимо заново скопировать нужные данные в массив dest_array[][]. При этом массив-приемник будет автоматически распределен под требуемое количество копируемых баров, даже если он был объявлен статически.

Изменился формат хранения исторических данных RateInfo. В старой версии состав структуры RateInfo был представлен таким образом:

struct RateInfo
<
unsigned int ctm ; // дата открытия бара
double open ; // цена открытия
double low ; // минимальная цена бара
double high ; // максимальная цена бара
double close ; // цена закрытия
double vol ; // объем
>;

В новом формате в структура RateInfo добавлены поля для хранения спреда и торгового объема:

//— Стандартное представление котировки в новой версии терминала
struct RateInfo
<
INT64 ctm ; // дата и время открытия
double open ; // цена открытия (абсолютное значение)
double high ; // минимальная цена бара
double low ; // максимальная цена бара
double close ; // цена закрытия
UINT64 vol ; // тиковый объём
INT32 spread ; // спред
UINT64 real ; // торговый объём
>;

Соответственно, если в программах MQL4 используются DLL для передачи/приема ценовых данных, то соответствующие функции в исходных кодах должны быть переписаны и заново скомпилированы с учетом изменении формата для обеспечения правильной работы.

Старые EX4 программы и DLL, основанные на старом формате RateInfo, работать в новом терминале не будут. Требуется приведение к новому формату.

В файловых операциях количество одновременно открытых файлов теперь может достигать 64, в старом MQL4 было не более 32. При этом раньше файлы всегда открывались в режиме FILE_SHARE_READ, FILE_SHARE_WRITE, теперь желательный режим открытия нужно указывать явно.

Для функций FileWrite(), FileWriteArray(), FileWriteDouble(), FileWriteInteger() и FileWriteString() изменен тип возвращаемого значения с int на uint. Данные функции возвращают количество записанных байт или 0 в случае ошибки (в старой версии MQL4 в случае ошибки возвращалось отрицательное число).

Изменилась также работа с функциями, областью видимости переменных и освобождением памяти в локальных массивах. Так как изменений оказалось достаточно много, то для обеспечения максимальной совместимости со старым подходом к написанию программ MQL4 было решено ввести новое свойство #property strict. При создании новой программы MQL4 с помощью Мастера MQL4 это свойство всегда добавляется в шаблон.

Следует иметь ввиду, что преобразование типа datetime в строку теперь зависит от режима компиляции:

datetime date=D’2014.03.05 15:46:58′;
string str= "mydate=" +date;
//— str="mydate=1394034418" — старый компилятор, новый компилятор без директивы #property strict
//— str="mydate=2014.03.05 15:46:58" — новый компилятор с директивой #property strict

Ниже в таблице перечислены все различия между старым MQL4, новым MQL4 без использования strict и новым MQL4 с указанием строгого режима компиляции:

При компиляции библиотек в режиме strict для каждой экспортируемой функции необходимо добавлять модификатор export. в противном случае функция не будет доступна извне.

Таблица отличий между компиляторами:

Старый компилятор MQL4

Новый компилятор MQL4

Новый компилятор MQL4 с #property strict

Точки входа init(), start(), deinit() могут иметь любые параметры и любой тип возврата

init(), start(), deinit(), оставленные для совместимости,

Результат возврата из функции init() никак не анализируется исполняющей подсистемой

Результат возврата из функций init() и OnInit() никак не анализируется исполняющей подсистемой

При возврате из OnInit() ненулевого значения выполнение эксперта или индикатора прекращается, программа выгружается

Имена переменных практически любые (кроме зарезервированных слов), включая спецсимволы и точки

Имена переменных не могут иметь спецсимволы и точки.

Список зарезервированных слов расширен, поэтому именами не могут быть такие широко распространенные слова, как short, long, const и т.д.

Область видимости переменной — от объявления (даже во вложенном блоке) до конца функции

Область видимости переменной — от объявления до конца блока, в котором переменная объявлена

Неявная инициализация всех переменных (и глобальных и локальных) нулями

Инициализация только глобальных переменных. Из локальных переменных неявно инициализируются только строки

Локальные массивы при выходе из функции не освобождаются

Локальные массивы освобождаются при выходе из функции

Локальные массивы освобождаются при выходе из блока <>

Ошибка "Array out of range" (индекс за пределами массива) не приводит к критической ошибке*

То же. Кроме массивов структур и классов, для которых эта ошибка является критической

"Array out of range" — критическая ошибка и приводит к остановке программы

Структур и классов нет

Структуры и классы есть. Появились дополнительные типы данных

datetime — 32-битное целое

Предопределенная переменная Volume имеет тип double

datetime — 64-битное целое

Предопределенная переменная Volume имеет тип long

ArrayCopyRates() производит виртуальное копирование в массив double[][6]

ArrayCopyRates() производит виртуальное копирование в массив MqlRates[]. Для совместимости осталось копирование в массив double[][6], но это копирование не виртуальное, а реальное.

Функции могут не возвращать значения, даже если они имеют тип. Для этого в конце функции компилятором автоматически проставляется return(0)

Функции любого типа всегда должны возвращать значение

Количество одновременно открытых файлов — 32

Количество одновременно открытых файлов — 64

Файлы всегда открываются в режиме совместного использования **

Режим совместного использования нужно указывать явно с помощью FILE_SHARE_READ и/или FILE_SHARE_WRITE

Для скриптов в окне входных параметров показываются имена extern переменных

Для скриптов в режиме show_inputs в окне входных параметров показываются имена extern и input-переменных

Для скриптов в режиме show_inputs в окне входных параметров показываются строчные комментарии вместо имен extern- и input-переменных

* Следует обратить особое внимание на ошибку "Array out of range" — многие старые пользовательские индикаторы при запуске на графике будут выдавать эту ошибку в новом компиляторе в режиме strict. Желательно найти причину и устранить её.

** В новом MQL4 и MQL5 за режим совместного использования файлов отвечают флаги FILE_SHARE_READ и FILE_SHARE_WRITE, которых не было в старом MQL4.

В предыдущих билдах клиентского терминала MetaTrader 4 (509 и ниже) все MQL4 программы хранились в корневой папке experts в следующих подпапках:

  • experts — эксперты (торговые роботы),
  • expertsindicators — пользовательские индикаторы,
  • expertsscripts — скрипты (программы на MQL4 для однократного выполнения на графике),
  • include — включаемые в другие программы файлы исходного кода с расширением MQH и MQ4,
  • libraries — библиотеки в виде исходного кода MQ4 и скомпилированные из них исполняемые файлы EX4, для динамического вызова собранных в них функций другими MQL4-программами,
  • files — специальная "файловая песочница", только в пределах этой папки MQL4 программам разрешается выполнять файловые операции.

В новой версии MQL4 файловая структура для хранения исходных кодов изменилась. Теперь все программы на MQL4 должны находиться в папке MQL4 во вложенных соответствующих папках:

  • Experts — эксперты (торговые роботы),
  • Indicators — пользовательские индикаторы,
  • Scripts — скрипты (программы на MQL4 для однократного выполнения на графике),
  • Include — включаемые в другие программы файлы исходного кода с расширением MQH и MQ4,
  • Libraries — библиотеки в виде исходного кода MQ4 и скомпилированные из них исполняемые файлы EX4, для динамического вызова собранных в них функций другими MQL4-программами,
  • Images — файлы картинок для использования в ресурсах,
  • Files — специальная "файловая песочница", только в пределах этой папки MQL4 программам разрешается выполнять файловые операции.

При обновлении терминала MetaTrader 4 с 509 билда на новую версию все файлы MQ4, MQH и EX4 из стандартных корневых папок старого местоположения автоматически копируются и раскладываются в соответствующие папки. Дополнительно созданные пользователем внутри стандартных папок дочерние папки с файлами никак не обрабатываются , их необходимо самостоятельно скопировать в новое место, если они необходимы.

Никакие файлы или папки при обновлении не удаляются! Все операции по копированию файлов при обновлении записываются в Журнал терминала с указанием подробных путей.

При обновлении автоматическая перекомпиляция старых EX4-файлы на новую версию не производится, пользователю предоставлена возможность самому решать какие исходные коды он хочет скомпилировать в новую версию EX4. Все старые EX4 будут работать и в новом терминале MetaTrader 4. Библиотеки EX4, скомпилированные новым компилятором, могут вызываться только из программ EX4, также скомпилированных в новой версии.

Возможно, в некоторых случаях в исходных файлах потребуется исправить пути в #include для включаемых файлов (если относительные пути изменились). Важно запомнить, что сейчас корневой папкой для редактора MetaEditor является папка MQL4, все программы должны находиться в правильных дочерних подпапках.

Найти каталог данных ( ) на компьютере для каждой копии установленного терминала MetaTrader 4 можно из меню терминала или редакторе MetaEditor: "Файл" — "Открыть каталог данных".

Введение

При использовании новой версии компилятора языка MQL4 некоторые старые программы могут выдавать ошибки.

В старой версии компилятора во избежание критического завершения программ многие ошибки обрабатывались средой исполнения и не приводили к остановке работы. Например, деление на ноль или выход за пределы массива являются критическими ошибками и обычно приводят к аварийному завершению. Проявляются такие ошибки лишь в некоторых состояниях при определенных значениях переменных, однако о таких ситуациях следует знать и корректно их обрабатывать.

Новый компилятор позволяет обнаружить реальные или потенциальные источники ошибок и повысить качество кода.

В этой статье мы рассмотрим возможные ошибки, возникающие при компиляции старых программ, и методы их устранения.

1. Ошибки компиляции

При наличии ошибок в коде программа не может быть скомпилирована.

Для полного контроля всех ошибок рекомендуется использовать строгий режим компиляции, который устанавливается директивой:

Этот режим значительно упрощает поиск ошибок.

1.1. Идентификатор совпадает с зарезервированным словом

Если наименование переменной или функции совпадает с одним из зарезервированных слов:

то компилятор выводит сообщения об ошибках:

Рис.1. Ошибки "unexpected token" и "name expected"

Для исправления данной ошибки нужно исправить имя переменной или функции.

1.2. Специальные символы в наименованиях переменных и функций

Если наименования переменных или функций содержат специальные символы ($, @, точка):

то компилятор выводит сообщения об ошибках:

Рис.2. Ошибки "unknown symbol" и "semicolon expected"

Для исправления данной ошибки нужно скорректировать имена переменных или функций.

1.3. Ошибки использования оператора switch

Старая версия компилятора позволяла использовать любые значения в выражениях и константах оператора switch:

В новом компиляторе выражения и константы оператора switch должны быть целыми числами, поэтому при использовании подобных конструкций возникают ошибки:

Рис.3. Ошибки "illegal switch expression type" и "constant expression is not integral"

В таких случаях можно использовать явные сравнения численных значений, например:

1.4. Возвращаемые значений функций

Все функции, кроме void, должны возвращать значение объявленного типа. Например:

При строгом режиме компиляции (strict) возникает ошибка:

Рис.4. Ошибка "not all control paths return a value"

В режиме компиляции по умолчанию компилятор выводит предупреждение:

Рис.5. Предупреждение "not all control paths return a value"

Если возвращаемое значение функции не соответствует объявлению:

то при строгом режиме компиляции возникает ошибка:

Рис.6. Ошибка "function must return a value"

В режиме компиляции по умолчанию компилятор выводит предупреждение:

Рис.7. Предупреждение ‘return — function must return a value"

Для исправления таких ошибок в код функции нужно добавить оператор возврата return c возвращаемым значением соответствующего типа.

1.5. Массивы в аргументах функций

Массивы в аргументах функций теперь передаются только по ссылке.

Данный код при строгом режиме компиляции (strict) приведет к ошибке:

Рис.8. Ошибка компилятора "arrays passed by reference only"

В режиме компиляции по умолчанию компилятор выводит предупреждение:

Рис.9. Предупреждение компилятора "arrays passed by reference only"

Для исправления таких ошибок нужно явно указать передачу массива по ссылке, добавив префикс & перед именем массива:

Следует отметить, что теперь константные массивы ( Time[], Open[], High[], Low[], Close[], Volume[]) не могут быть переданы по ссылке. Например, вызов:

вне зависимости от режима компиляции приводит к ошибке:

Рис.10. Ошибка ‘Open’ — constant variable cannot be passed as reference

Для устранения подобных ошибок нужно скопировать необходимые данные из константного массива:

2. Ошибки времени выполнения

Ошибки, возникающие в процессе исполнения кода программы принято называть ошибками времени выполнения (runtime errors). Такие ошибки обычно зависят от состояния программы и связаны с некорректными значениями переменных.

Например, если переменная используется в качестве индекса элементов массива, то ее отрицательные значения неизбежно приведут к выходу за пределы массива.

2.1. Выход за пределы массива (Array out of range)

Эта ошибка часто возникает в индикаторах при обращении к индикаторным буферам. Функция IndicatorCounted() возвращает количество баров, неизменившихся после последнего вызова индикатора. Значения индикаторов на уже рассчитанных ранее барах не нуждаются в пересчете, поэтому для ускорения расчетов достаточно обрабатывать только несколько последних баров.

Большинство индикаторов, в которых используется данный способ оптимизации вычислений, имеют вид:

Часто встречается некорректная обработка случая counted_bars==0 (начальную позицию limit нужно уменьшить на значение, равное 1 + максимальный индекс относительно переменной цикла).

Также следует помнить о том, что в момент исполнения функции start() мы можем обращаться к элементам массивов индикаторных буферов от 0 до Bars()-1. Если есть необходимость работы с массивами, которые не являются индикаторными буферами, то их размер следует увеличить при помощи функции ArrayResize() в соответствии с текущим размером индикаторных буферов. Максимальный индекс элемента для адресации также можно получить вызовом ArraySize() с одним из индикаторных буферов в качестве аргумента.

2.2. Деление на ноль (Zero divide)

Ошибка "Zero divide" возникает в случае, если при выполнении операции деления делитель оказывается равен нулю:

При выполнении данного скрипта во вкладке "Эксперты" возникает сообщение об ошибке и завершении работы программы:

Рис.11. Сообщение об ошибке "zero divide"

Обычно такая ошибка возникает в случаях, когда значение делителя определяется значениями каких-либо внешних данных. Например, если анализируются параметры торговли, то величина задействованной маржи оказывается равна 0 если нет открытых ордеров. Другой пример: если анализируемые данные считываются из файла, то в случае его отсутствия нельзя гарантировать корректную работу. По этой причине желательно стараться учитывать подобные случаи и корректно их обрабатывать.

Самый простой способ — проверять делитель перед операцией деления и выводить сообщение об некорректном значении параметра:

В результате критической ошибки не возникает, но выводится сообщение о некорректном значении параметра и работа завершается:

Рис. 12. Сообщение о некорректном значении делителя

2.3. Использование 0 вместо NULL для текущего символа

В старой версии компилятора допускалось использование 0 (нуля) в качестве аргумента в функциях, требующих указания финансового инструмента.

Например, значение технического индикатора Moving Average для текущего символа можно было запрашивать следующим образом:

В новом компиляторе для указания текущего символа нужно явно указывать NULL:

Кроме того, текущий символ и период графика можно указать при помощи функций Symbol() и Period().

2.4. Строки в формате Unicodе и их использование в DLL

Строки теперь представляют собой последовательность символов Unicode.

Следует учитывать этот факт и использовать соответствующие функции Windows. Например, при использовании функций библиотеки wininet.dll вместо InternetOpenA() и InternetOpenUrlA () следует вызывать InternetOpenW() и InternetOpenUrlW().

В MQL4 изменилась внутренняя структура строк (теперь она занимает 12 байт), поэтому при передаче строк в DLL следует использовать структуру MqlString:

2.5. Совместное использование файлов

В новом MQL4 при открытии файлов необходимо явно указывать флаги FILE_SHARE_WRITE и FILE_SHARE_READ для совместного использования.

В случае их отсутствия файл будет открыт в монопольном режиме, что не позволит больше никому его открывать, пока он не будет закрыт монополистом.

Например, при работе с оффлайновыми графиками требуется явно указывать флаги совместного доступа:

Подробности можно найти в статье в статье "Оффлайновые графики и новый MQL4".

2.6. Особенность преобразования datetime

Следует иметь ввиду, что преобразование типа datetime в строку теперь зависит от режима компиляции:

Например, попытка работы с файлами, имя которых содержит двоеточие, приведет к ошибке.

3. Предупреждения компилятора

Предупреждения компилятора носят информационный характер и не являются сообщениями об ошибках , однако они указывают на возможные источники ошибок и лучше их скорректировать.

Чистый код не должен содержать предупреждений.

3.1. Пересечения имен глобальных и локальных переменных

Если на глобальном и локальном уровнях присутствуют переменные с одинаковыми именами:

то компилятор выводит предупреждение и укажет номер строки, на которой объявлена глобальная переменная:

Рис.13. Предупреждение "declaration of ‘%’ hides global declaration at line %"

Для исправления таких предупреждений нужно скорректировать имена глобальных переменных.

3.2. Несоответствие типов

В новой версии компилятора в ведена операция приведения типов.

В строгом режиме компиляции при несоответствии типов компилятор выводит предупреждения:

Рис.14. Предупреждения "possible loss of data due to type conversion" и "implicit conversion from ‘number’ to ‘string’

В данном примере компилятор предупреждает о возможной потере точности при присвоении различных типов данных и неявном преобразовании типа int в string.

Для исправления нужно использовать явное приведение типов:

3.3. Неиспользуемые переменные

Наличие переменных, которые не используются в коде программы (лишние сущности) не является хорошим тоном.

Сообщения о таких переменных выводятся вне зависимости от режима компиляции:

Рис.15. Предупреждения "variable ‘%’ not used’

Для исправления нужно убрать неиспользуемые переменные из кода программы.

Выводы

В статье рассмотрены типичные проблемы, с которыми могут столкнуться программисты при компиляции старых программ, содержащих ошибки.

Во всех случаях при отладке программ рекомендуется использовать строгий режим компиляции.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *