Различия

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

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

atcl:howto [2018/01/26 07:35] (текущий)
nektomk создано
Строка 1: Строка 1:
 +===== Краткий HowTo =====
 +
 +До любого обращения к ATcl должна быть вызвана функций [[ATcl_OnInit]]() и в завершении скрипта/​индикатора/​советника [[ATcl_OnDeinit]](const int reason). Между вызовами этих двух функций возможны обращения ко всем прочим функциям библиотеки. При разработке советников и индикторов также рекомендуется вызывать функции [[ATcl_OnEvent]]() ATcl_OnTimer() ATcl_OnTick() внутри соответсвующих функций MQL.
 +
 +Все функции Tcl доступны через интерпретаторы,​ которые представлены классом ATcl. Вы можете создавать несколько интерпретаторов,​ не обязательно чтобы он был один, главное чтобы все вызовы были между [[ATcl_OnInit]] и [[ATcl_OnDeinit]].
 +
 +И после такой вводной можно уже и иллюстрировать на MQL:
 +<code cpp>
 +#include <​ATcl.mqh>​
 +void OnStart() {
 +  ATcl_Init();​ ///​ начинаем использовать ATcl
 +  ATcl *tcl= new ATcl; /// создадим интерпретатор
 +  Print(tcl.StringEval("​info tclversion"​));​ ///​ выведем в консоль версию tcl
 +  delete tcl; /// удалим интерпретатор
 +  ATcl_Deinit();​ ///​ завершаем использовать ATcl
 +}
 +</​code>​
 +
 +Конечно перед тем как это использовать надо [[install|Установить всё]] :-) то есть поставить Tcl в систему и разместить ATcl.mqh , ATcl.dll по нужным путям.
 +
 +Для того чтобы пользоваться было легко и просто,​ реализованы методы [[Eval|StringEval]],​ [[Eval|DoubleEval]],​ [[Eval|IntEval]],​ - которые исполняют некий скрипт и возвращают требуемое по логике значение. Все эти (а далее возможно и другие) являются обёрткой над общим методом [[Eval]](script,​flags) который исполняет заданный скрипт и возвращает код результата ( [[TCL_RET|TCL_OK]] или иной. ​
 +
 +Если переписать пример с максимальным контролем,​ то будет :
 +<code cpp>
 +#include <​ATcl.mqh>​
 +void OnStart() {
 +  /// начинаем использовать ATcl
 +  if (ATcl_Init()!=INIT_SUCCESUL) {
 +    // некая ошибка при интициализации библиотеки
 +    Alert("​Error in ATcl_Init()"​);​
 +    return;
 +  }
 +  /// создадим интерпретатор
 +  ATcl *tcl= new ATcl;
 +  if (tcl == NULL || !tcl.Ready()) {
 +    // ошибка при создании интерпретатора
 +    Alert("​Error in 'new ATcl'"​);​
 +    return
 +  }
 +  if (tcl.Eval("​info tcl_version"​)!=TCL_OK) {
 +    // Ошибка при исполнении команды
 +    Alert(StringFormat("​Error in script: %s",​tcl.StringResult()));​
 +  } else {
 +    // команда исполнена ​
 +    Print("​Tcl version=%s",​tcl.StringResult());​
 +  }
 +  /// звершаем работу
 +  delete tcl; /// удалим интерпретатор
 +  ATcl_Deinit();​ ///​ завершаем использовать ATcl
 +}
 +</​code>​
 +
 +В общем работать с библиотекой довольно просто,​ методы XxxEval исполняют скрипты Tcl, методы Typename(obj) конвертируют объекты Tcl в значения Mql, методы Obj(x) создают объекты Tcl из значений. Дополнительно методы Set,Get позволяют задавать и менять значения переменных Tcl. 
 +
 +===== Более серьёзный пример =====
 +
 +Для более серьёзной демонстрации будет скрипт который сохраняет котировки в базу данных. Возьмём встроенную базу sqlite3, хотя при наличии желания можно и другие (mysql,​postgress,​jdbc,​monetdb активно поддерживаются,​ также можно подключать ODBC). У Oracle свой интерфейс представленный отдельным пакетом oratcl. ​
 +<code cpp>
 +//​+------------------------------------------------------------------+
 +//|                                                  atcl_sqlite.mq4 |
 +//|                                                Maxim A.Kuznetsov |
 +//|                                                      luxtrade.tk |
 +//​+------------------------------------------------------------------+
 +#property copyright "Maxim A.Kuznetsov"​
 +#property link      "​luxtrade.tk"​
 +#property version ​  "​1.00"​
 +#property strict
 +
 +#include "​ATcl.mqh"​
 +
 +const string Shema = 
 +"​CREATE TABLE IF NOT EXISTS Bar ("
 +" ​  ​symbol TEXT,"
 +" ​  ​period TEXT,"
 +" ​  time INTEGER NOT NULL,"
 +" ​  open REAL NOT NULL, "
 +" ​  high REAL NOT NULL, "
 +" ​  low REAL NOT NULL,  "
 +" ​  close REAL NOT NULL,"
 +" ​  ​volume INTEGER NOT NULL, "
 +" ​  ​PRIMARY KEY(symbol,​period,​time)"​
 +" )";
 +void OnStart()
 +{
 +   ​ATcl_OnInit();​ // включить ATcl
 +   
 +   ATcl *tcl=new ATcl; // создать интерпретатор
 +   if (tcl==NULL || !tcl.Ready()) {
 +      Alert("​Ошибка при создании интерпретатора"​);​
 +      ATcl_OnDeinit();​
 +      return;
 +   }
 +   int ok=false;
 +   do { 
 +      Print("​Берём встроенный SQLite"​);​
 +      if (tcl.Eval("​package require tdbc::​sqlite3"​)!=TCL_OK) break;
 +      Print("​Считаем путь к базе"​);​
 +      tcl.Set("​dbname",​tcl.Obj("​bar.db3"​));​
 +      tcl.Set("​datadir",​tcl.Obj(TerminalInfoString(TERMINAL_DATA_PATH)));​
 +      if (tcl.Eval("​set fullPath [ file join $datadir MQL4 Files $dbname ]"​)!=TCL_OK) break;
 +      PrintFormat("​Открываем базу %s",​tcl.String(tcl.Get("​fullPath"​)));​
 +      if (tcl.Eval("​tdbc::​sqlite3::​connection create db $fullPath"​)!=TCL_OK) break;
 +      Print("​Задаём схему"​);​
 +      if (tcl.Eval(StringFormat("​db allrows {%s}",​Shema))!=TCL_OK) break;
 +      Print("​Готовим стейтмент"​);​
 +      if (tcl.Eval("​set stmt [ db prepare {REPLACE INTO Bar (symbol,​period,​time,​open,​high,​low,​close,​volume) VALUES (:​symbol,:​period,:​t,:​o,:​h,:​l,:​c,:​v)} ]"​)!=TCL_OK) break;
 +      Print("​Делаем переменные"​);​
 +      tcl.Set("​symbol",​tcl.Obj(Symbol()));​
 +      tcl.Set("​period",​tcl.Obj(EnumToString((ENUM_TIMEFRAMES)Period())) );
 +      tcl.Set("​time",​tcl.Obj(Time));​
 +      tcl.Set("​open",​tcl.Obj(Open));​
 +      tcl.Set("​high",​tcl.Obj(High));​
 +      tcl.Set("​low",​tcl.Obj(Low));​
 +      tcl.Set("​close",​tcl.Obj(Close));​
 +      tcl.Set("​volume",​tcl.Obj(Volume));​
 +      tcl.Set("​n",​tcl.Obj((long)0));​
 +      Print("​Запускаем стейтмент по массивам"​);​
 +      // скрипт как объект,​ чтобы скомпилялся
 +      Tcl_Obj f=tcl.Obj("​foreach t $time o $open h $high l $low c $close v $volume { $stmt execute ; incr n ; if {$n==100} break }");
 +      tcl.Ref(f);
 +      if (tcl.Eval(f)!=TCL_OK) break;
 +      tcl.Unref(f);​
 +      Print("​Удаляем стейтмент"​);​
 +      tcl.Eval("​$stmt close"​);​
 +      Print("​Закрываем базу"​);​
 +      tcl.Eval("​$db close"​);​
 +      ok=true;
 +   ​}while(false);​
 +   if (!ok) {
 +      PrintFormat("​Что-то пошло не так: %s",​tcl.StringResult());​
 +   }
 +   ​delete tcl;          // удалить интерпретатор
 +   ​ATcl_OnDeinit(); ​ // выключить ATcl
 +}
 +
 +</​code>​
 +
 +===== Eval,Call: исполнение скриптов и команд =====
 +Исполнение скриптов Tcl в интерпретаторе. Если в качестве первого параметра будет передан Tcl_Obj, то он будет предварительно скомпиллирован. ​
 +<code cpp>
 +   int Eval(Tcl_Obj,​ int flags=0); /// исполнить скрипт Tcl и получить код успеха. Результат будет доступен через Result
 +   int Eval(string &,int flags=0);
 +   ​double DoubleEval(Tcl_Obj,​int flags)=0; /// исполнить скрипт и сразу получить результат. При ошибке получится 0.0
 +   ​double DoubleEval(string &,int flags=0);
 +   ​string StringEval(Tcl_Obj,​int flags=0); /// исполнить скрипт и сразу получить результат. При Ошибке будет полуен ""​
 +   ​string StringEval(string &,int flags=0);
 +   int Call(Tcl_Obj command,​...);​ //​ исполнить команду с аргументами Tcl_Obj (до 10-ти шт)
 +   int Call(Tcl_Obj &​objv[],​int objc,int flags); // исполнить команду с аргументами,​ переданными через массив
 +</​code>​
 +Главным методом является Eval - исполнить скрипт заданный в объекте и вернуть код результата. Сам по себе результат будет доступен через [[Result]]. Могут быть возращены коды TCL_OK - если всё правильно,​ TCL_ERROR - в случае ошибки,​ TCL_BREAK,​TCL_CONINUE в особых случаях
 +
 +Типичный Use-case :
 +<code cpp>
 +   ​ATcl_Init();​ //​ включить системы
 +   ATcl *tcl=new ATcl; // создать интерпретатор
 +   ....
 +   ​Tcl_Obj script=tcl.Obj("​clock seconds"​);​ // создать объект
 +   ​tcl.Ref(script);​ //​ увеличить счётчик ссылок,​ чтобы он не был разрушен после первого использования
 + // исполнить скрипт
 +   if (tcl.Eval(script)!=TCL_OK) {
 +    // какая-то ошибка,​ дать Alert
 +        Alert(StringFormat("​Error in Eval():​%s",​tcl.StringResult()) );
 +   } else {
 +        // ошибок при исполнении нет, можно пользоваться результатом
 +        // (теперь script скомпилирован и исполнен)
 +        PrintFormat("​UNIXTIME:​ %d",​tcl.LongResult());​
 +   }
 +   ....
 +   ​tcl.Unref(script);​ // объекты к которым применён ранее Ref() надо Unref() чтобы они удалились
 +   ....
 +   ​delete tcl;   // удаляем интерпретатор
 +   ​ATcl_Deinit();​ // выключаем систему
 +</​code>​
 +
 +Чтобы не писать таких длинных "​простыней"​ добавлены простые методы для исполнения скриптов,​ например DoubleEval - исполнить скрипт и получить результат как double. И скрипт можно передавать как объектом,​ так и обычной строкой.
 +<​code>​
 +   ​PrintFormat("​sqrt(2)=%f",​tcl.DoubleEval("​expr sqrt(2)"​));​
 +</​code>​
 +<​todo>​ Source - исполнить файл (с учётом флага Common)</​todo>​
 +
 +===== Result: получение результатов =====
 +
 +Методы Result используются для получения последнего результата. ​
 +<code cpp>
 +   ​Tcl_Obj Result(int code=0); // получить результат в виде объекта
 +   ​double DoubleResult(int code=0); // получить результат в виде double
 +   ​string StringResult(int code=0); ​    // получить результат как string
 +</​code>​
 +Аргумент code игнорируется и служит для возможности "​вложений"​ внутрь вызова Eval:
 +<code cpp>
 +   if (tcl.Eval("​array get tcl_platform"​)!=TCL_OK) {
 +    // какая-то ошибка,​ получим её описание
 +        Alert(StringFormat("​Error : %s",​tcl.StringResult());​
 +   } else {
 +        // ошибок нет, печатаем инфорацию о платформе
 +        Print(tcl.StringResult());​
 +   }
 +   // использование "​вложенности" ​
 +   ​PrintFormat("​Version:​ %f",​tcl.DoubleResult(tcl.Eval("​set tcl_version"​)) );
 +</​code>​
 +
 +===== Obj: создание объектов ​ =====
 +
 +Методы Obj() используются для преобразования данных Mql в объекты Tcl. Создают новый объект со 0-м счётчиком ссылок. То есть созданный объект будет автоматически удалён после первого использования. Для многократного использования используется метод Ref(obj), который этот счётчик увиличит. Для освобождения объекта соотв. Unref(obj).
 +<code cpp>
 +   /// создание объектов
 +   ​Tcl_Obj Obj(); // создать пустой объект
 +   ​Tcl_Obj Obj(string);​ //​ создать объект из строки
 +   ​Tcl_Obj Obj(double);​ // создать объект из double
 +   ​Tcl_Obj Obj(long);​ //​ создать объект из long
 +   ​Tcl_Obj Obj(datetime);//​ из datetime
 +   /// управление ссылками
 +   ​Tcl_Obj Ref(Tcl_Obj obj); // увеличить счётчик и вернуть тот-же объект
 +   ​Tcl_Obj Unref(Tcl_Obj obj); // уменьшить счётчик ссылок и вернуть объект;​ 0 если счётчик обнулился и объект был удалён
 +</​code>​
 +
 +===== String,​Double:​ получение значений =====
 +
 +Методы String,​Double,​Long используются для преобразования объектов Tcl в соответствующие значения Mql. 
 +<code cpp>
 +   ​string String(Tcl_Obj); ​ // получить string из объекта
 +   ​string String(Tcl_Obj,​int index); // получить string из элемента списка по индексу
 +   ​double Double(Tcl_Obj); ​ // получить double из объекта
 +   ​double Double(Tcl_Obj,​int index); // получить double из элемента списка по индексу
 +   ​long ​  ​Long(Tcl_Obj); ​               // аналогично - получение Long
 +   ​long ​  ​Long(Tcl_Obj,​int index);
 +   ​datetime Datetime(Tcl_Obj); ​         // то-же но datetime
 +   ​datetime Datetime(Tcl_Obj,​index);​
 +</​code>​
 +При обращении к списку по индексу,​ элементы списка нумеруется от 0 (0-самый первый). Отрицательные индесы адреусуют элементы от конца списка (-1 - саый последний).
 +
 +К примеру:​
 +<code cpp>
 +   ​Tcl_Obj list=tcl.Obj("​alpha beta gamma theta"​);​
 +   ​tcl.Ref(list);​
 +   ​PrintFormat("​List elements: first=%s last=%s",​tcl.String(list,​0),​tcl.String(list,​-1));​
 +   ​tcl.Unref(list);​
 +</​code>​
 +
 +===== ListLength, ListIndex, Count: списки =====
 +
 +Небольшой интерфейс к спискам Tcl. 
 +<code cpp>
 +    int ListLength(Tcl_Obj);​ // получить кол-во элементов списка
 +    Tcl_Obj ListIndex(Tcl_Obj,​int index); // получить элемент списка по индексу
 +    int Count(Tcl_Obj);​ //​ синоним ListLength, для любителей C#
 +    int ListAppend(Tcl_Obj list,​Tcl_Obj element); // добавить элемент в конец списка
 +    int Append(Tcl_Obj list,​Tcl_Obj element); ​  // синоним ListAppend
 +</​code>​
 +Элементы списков нумеруются от 0 (0-самый первый элемент). При указание отрицательного индекса,​ элементы выбираются от конца списка (-1 - самый последний)
 +
 +<​todo>​ Добавить вторичный индекс для обращений к спискам-списов и элементам матриц</​todo>​
 +
 +===== ToArray, FromArray: работа с массивами =====
 +
 +Для создание объектов из массивов используется расширенный метод Obj. Для получение данных из объектов в массивы метод ToArray. Синтаксис ToArray масксимально близок к CopyArray
 +<code cpp>
 +    Tcl_Obj Obj(double &​array[],​int pos=0,int count=WHOLE_ARRAY);//​ создание объекта из массива double
 +    Tcl_Obj Obj(string &​array[],​int pos=0,int count=WHOLE_ARRAY);//​ создание объекта из массива string
 +    Tcl_Obj Obj(long &​array[],​int pos=0,int count=WHOLE_ARRAY);//​ создание объекта из массива long
 +    Tcl_Obj Obj(datetime &​array[],​int pos=0,int count=WHOLE_ARRAY);//​ создание объекта из массива datetime
 +    int ToArray(double &​dst[],​Tcl_Obj src,int dst_pos=0,​int src_pos=0,​int count=WHOLE_ARRAY);​
 +    int ToArray(string &​dst[],​Tcl_Obj src,int dst_pos=0,​int src_pos=0,​int count=WHOLE_ARRAY);​
 +    int ToArray(long &​dst[],​Tcl_Obj src,int dst_pos=0,​int src_pos=0,​int count=WHOLE_ARRAY);​
 +</​code>​
 +<​todo>​если будет востребовано,​ использовать tarray или vectcl (оба - для быстрых и компактных операций с массивами)</​todo>​
 +
 +===== Set,​Get,​Unset:​ доступ к переменным =====
 +
 +Хотя управление переменными возможно через Eval, для удобства и скорости добавленны методы Set,​Get,​Unset
 +<code cpp>
 +   ​Tcl_Obj Set(Tcl_Obj name,​Tcl_Obj value); // задать значение переменной name
 +   ​Tcl_Obj Set(Tcl_Obj hash,​Tcl_Obj key, Tcl_Obj value); // задать значение элемента массива
 +   ​Tcl_Obj Get(Tcl_Obj name); // получить значение переменной
 +   ​Tcl_Obj Get(Tcl_Obj hash,​Tcl_Obj key); // получить значение элемента массива
 +</​code>​
 +
 +<​todo>​Unset - удаление переменных</​todo>​
 +
 +<​todo>​IsSet,​ IsArray - проверки на существование переменных/​массивов</​todo>​
 +
 +<​todo>​прозрачная поддержка словарей наравне с массивами</​todo>​
 +
 +===== Practice =====
 +
 +Рекомендации по практическому использованию ATcl см. на странице [[Practice]]
 +
 +===== Install =====
 +
 +Чтобы всё работало,​ во первых надо поставить Tcl. Для работы в MT4 должны быть установлена 32-х битная версия. Дистрибутив включающий Tcl/Tk с кучей доп.библиотек и пакетов можно взять у ActiveState (www.ActiveState.com) или MagicSplat (www.MagicSplat.com)
 +
 +И конечно нужны ATcl.mqh и ATcl.dll, они вот тут в одном архиве : {{ :​atcl:​atcl.zip |}}