В честь состоявшегося релиза ATcl - краткий пример, как с его помощью делается взаимодействие разных программ
Будем использовать "Thread Shared Variables" - переменные разделяемые между нитями одного приложения.
В Tcl это реализуется в пакете Thread, командами tsv::* https://www.tcl-lang.org/man/tcl9.0/ThreadCmd/tsv.html
В отличии от TerminalGlobalVariables там можно сказать двух-уровневая адресация "имя_переменной" + "имя элемента" и сохранять можно не только double, а любые данные. И всё только в памяти, автоматически на диск не сохраняется (хотя и это можно выборочно настроить)
Дальше проще всего пояснять на примере.
Набросаем простой советник, который:
1) считает и публикует пришедшие тики
2) принимает сообщения и что-то с ними делает. Просто печатает.
#include <ATcl/ATcl.mqh> input string KEY="tsvdemo"; ATcl *tcl=NULL; // литералы, то есть имена-строки которыми часто пользуемся Tcl_Obj str_key; Tcl_Obj str_tickCounter; Tcl_Obj str_tickInfo; Tcl_Obj str_incoming; Tcl_Obj proc_tsvset; Tcl_Obj proc_tsvincr; Tcl_Obj proc_tsvpop; int OnInit() { // инит библиотеки ATcl_OnInit(); // создаём интерпретатор (в принципе их можно сделать много) tcl = new ATcl(); tcl.OnInit(); // создаём литералы str_key=tcl.Ref(tcl.Obj(KEY)); str_tickCounter=tcl.Ref(tcl.Obj("tickCounter")); str_tickInfo=tcl.Ref(tcl.Obj("tickInfo")); str_incoming=tcl.Ref(tcl.Obj("incoming")); proc_tsvset=tcl.Ref(tcl.Obj("tsv::set")); proc_tsvincr=tcl.Ref(tcl.Obj("tsv::incr")); proc_tsvpop=tcl.Ref(tcl.Obj("tsv::pop")); // нам нужен пакет Thread tcl.Eval("package require Thread"); // зададим в интерпретаторе значение из input tcl.Set("KEY",KEY); // инициализируем данные // "атомарно для KEY: если нет поля tickCounter то создать его с 0" tcl.Eval("tsv::lock $KEY { if { ! [ tsv::exists $KEY tickCounter ] } { tsv::set $KEY tickCounter 0 } }"); // запустим таймер - по нему будем считывать входящие сообщения EventSetTimer(60); return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { if (tcl!=NULL) { // удаляем литералы tcl.Unref(proc_tsvpop); tcl.Unref(proc_tsvincr); tcl.Unref(proc_tsvset); tcl.Unref(str_key); tcl.Unref(str_tickCounter); tcl.Unref(str_incoming); // деинит интерпретатора tcl.OnDeinit(reason); delete tcl; tcl=NULL; } // деинит библиотеки ATcl_OnDeinit(reason); } void OnTick() { // для демонстрации: будем считать вызовы OnTick и писать последний тик tcl.Call(proc_tsvincr,str_key,str_tickCounter); // увеличиваем счётчик MqlTick tick; if (SymbolInfoTick(_Symbol,tick)) { // записываем инфу про тик // tsv::set $KEY tickInfo [ list $_Symbol $timeMsc $bid $ask ] tcl.Call(proc_tsvset,str_key,str_tickInfo,tcl.List(tcl.Obj(_Symbol),tcl.Obj(tick.time_msc),tcl.Obj(tick.bid),tcl.Obj(tick.ask))); } } void OnTimer() { // для демонстрации - считываем всё из очереди и реагируем // tsv::pop $KEY incoming if (tcl.Call(proc_tsvpop,str_key,str_incoming)!=TCL_OK) { return; } Tcl_Obj msgs = tcl.Ref(tcl.Result()); int total=tcl.Count(msgs); // всего получено сообщений из очереди // цикл по всем полученным сообщениям for(int i=0;i<total;i++) { int code = (int)tcl.Long(msgs,i,0); // код сообщения. i-е сообщение из msgs, элемент 0 switch(code) { case 1: // код 1 - просто печатаем Print(tcl.String(msgs,i,1)); // текст сообщения. i-е сообщение из msgs, элемент 1 break; case 2: // код 2 - выдаём алерт Alert(tcl.String(msgs,i,1)); break; } } tcl.Unref(msgs); }
Сделали, запускаем на свободном чарте.
Откроем ещё один чарт и запустим там советник-консоль из состава ATcl. Для теста - будет играть роль второго компонента
И будем вводить в консоль команды:

подключаем пакет Thread: package require Thread
смотрим есть-ли переменные с нужным именем (по умолчанию было tsvdemo): tsv::names tsvd*
смотрим какие есть элементы : tsv::array names tsvdemo
читаем счётчик и инфу о тике: tsv::get tsvdemo tickCounter , tsv::get tsvdemo tickInfo
и наконец-то посылаем в советник (в очередь incoming) несколько сообщений вида { код текст }:
tsv::lpush tsvdemo incoming [ list 1 "Hello from console" ]
или так:
tsv::lpush tsvdemo incoming { 2 "test alert" }собственно всё..Данные из любой другой программы то есть советника/индикатора/скрипта мы прочитать можем и организовать их общение тоже.
Надо только продумать систему именований и форматы.
Всех конечно интересует насколько быстро всё работает.
Ну так вот быстро:

на картинке - справа отображён полный биржевой стакан, который в базе в сжатом виде занимает 40-50k (а тут передаётся не сжатым), 10 раз/сек
а снизу объёмы сделок и скользящая сумма, сделок прилетает до 10 тыс/минуту
ничего не тормозит и подчас опережает "путь через терминал"
----
Релиз ATcl 1.11 https://www.mql5.com/ru/blogs/post/769153



