ATcl - MT4 için Tcl yorumlayıcısı

 

Tatiller verimli geçti ve MT4'teki yerleşik Tcl yorumlayıcısı olan ATcl'yi halka sunuyorum.

Projenin fikri, Tcl C Api işlevlerini doğrudan taşımadan yorumlayıcıya en uygun arayüzü yapmaktı (ve öyle).

Şöyle ortaya çıktı (çalışan bir örnek, bir alıntıyı SQLite veritabanına kaydeden bir komut dosyasıdır):

 //+------------------------------------------------------------------+
//|                                                  atcl_sqlite.mq4 |
//|                                                Maxim A.Kuznetsov |
//|                                                      luxtrade.tk |
//+------------------------------------------------------------------+
#property copyright "Maxim A.Kuznetsov"
#property link        "luxtrade.tk"
#property version    "1.00"
#property strict
#property script_show_inputs
#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
}

Bu aşamada, bu hala yoğun bir alfadır veya API'nin ne kadar kullanışlı olduğuna dair bir değerlendirme olan "API Önizlemesi" diyebilirsiniz. Ancak gördüğünüz gibi, DBMS arayüzleri biraz özenle çalışıyor, EventLoop da çalışıyor, yani asenkron komutları, giriş/çıkış ve zamanlayıcıları kullanabilirsiniz.


Ekli güncel sürüm ile arşivi deneyebilirsiniz..

Mümkün olduğunca desteklediğim belgeler ve en son sürümleri http://luxtrade.tk/atcl proje sayfasından alınabilir.

Bu başlıkta Tcl/Tk'nin kendisi ve bahsi geçen ATcl kütüphanesi ile ilgili soruları cevaplamaya hazırım. Fikir, öneri, eleştiri duymaktan memnuniyet duyacağım.

GÜNCELLEME: Konuda verilen betiğin kaynağını ekledim
GÜNCELLEME: atcl.zip güncellendi

Dosyalar:
atcl.zip  9 kb
 

İlginç bir şey.

Her zamanki gibi, R fikriyle uğraşıyorum.Bence en azından bu fikri tartışmak için doğru kişi sensin.


Fikir şudur.

Bugün, terminal grafiğinde, µl4/5 ile bir derleyici oluşturan yürütülebilir bir dosyayı sürükleyebilirsiniz.

R betiğini programa bağlayabilmek için bir geliştirme yapmak mümkün müdür?

 
СанСаныч Фоменко :

İlginç bir şey.

Her zaman olduğu gibi, R fikriyle uğraşıyorum . Bana öyle geliyor ki, en azından fikri tartışabileceğiniz kişi sizsiniz.


Fikir şudur.

Bugün, terminal grafiğinde, µl4/5 ile bir derleyici oluşturan yürütülebilir bir dosyayı sürükleyebilirsiniz.

R betiğini programa bağlayabilmek için bir geliştirme yapmak mümkün müdür?

Bu arada, R hakkında :-) R dağılımının bileşimine dikkat ettiniz mi?

ve böylece yerleşik R tcl'yi içeren bir kitaplık/tcltk dizini vardır. Bir dil/platformda yeterli kaynak bulunmadığında, ona "Araç Ortak Dili" olarak da bilinen tcl eklenir. R'de yeterli GUI yoktu ve içine tcl / tk kuruldu (aslında Python ve Ruby'de olduğu gibi).

MQL'de belki çizelgeler dışında pek çok özellikten yoksundum ve bu nedenle ATcl'yi aldım.

 

ATcl istatistik de yapabilir :-) Bir küçük test örneği daha temel istatistiklerin hesaplanmasıdır. veri kümesi için değerler. Komut dosyasında kapatılır, giriş parametresinde kaç tane ayarlanacak

 #property copyright "Maxim A.Kuznetsov"
#property link        "luxtrade.tk"
#property version    "1.00"
#property strict
#property script_show_inputs
//--- input parameters
input int       DEPTH= 200 ;

#include "ATcl.mqh"
const string NAMES[]={
   "mean" , "minimum" , "maximum" , "number of data" , "sample standard deviation" , "sample variance" , "population standard deviation" , "population variance"
};
void OnStart ()
{
   ATcl_OnInit();
   ATcl *tcl= new ATcl;
   if (tcl== NULL || !tcl.Ready()) {
      ATcl_OnDeinit();
       Alert ( "Ошибка при создании интерпретатора" );
   }
   bool ok= false ;
   do {
       if (tcl.Eval( "package require math::statistics" )!=TCL_OK) break ;
      tcl.Set( "data" ,tcl.Obj( Close , 0 ,DEPTH));
       if (tcl.Eval( "math::statistics::basic-stats $data" )!=TCL_OK) break ;
      Tcl_Obj stats=tcl.Result();
      tcl.Ref(stats);
       int total=tcl.Count(stats);
       for ( int i= 0 ;i<total;i++) {
         PrintFormat ( "stats %d \"%s\" = %s" ,i,(i< ArraySize (NAMES)?NAMES[i]: "??" ),tcl.String(stats,i));
      }
      tcl.Unref(stats);
      ok= true ;
   } while ( false );
   if (!ok) {
       PrintFormat ( "Что-то пошло не так : %s" ,tcl.StringResult());
   }
   delete tcl;
   ATcl_OnDeinit();
}


Senaryoyu ekliyorum. Tek bir hesaplama satırı var :-)

Dosyalar:
 

Ve bir örnek daha :-)

Bir zip alıp içindeki dosyaların sağlama toplamlarını hesaplayalım. MD5 ve SHA256'yı hesaplayacağız

 #property copyright "Maxim A.Kuznetsov"
#property link        "luxtrade.tk"
#property version    "1.00"
#property strict
#property script_show_inputs
//--- input parameters
input string    ZIP= "MQL4\\Scripts\\atcl.zip" ;
#include "ATcl.mqh"
void OnStart ()
{
   ATcl_OnInit();
   ATcl *tcl= new ATcl;
   if (tcl== NULL || !tcl.Ready()) {
      ATcl_OnDeinit();
       Alert ( "Ошибка при создании интерпретатора" );
   }
   bool ok= false ;
   do {
       if (tcl.Eval( "package require vfs::zip" )!=TCL_OK) break ;
       if (tcl.Eval( "package require md5" )!=TCL_OK) break ;
       if (tcl.Eval( "package require sha256" )!=TCL_OK) break ;
      tcl.Set( "archive" ,tcl.Obj(ZIP));
       if (tcl.Eval( "set z [ vfs::zip::Mount $archive $archive ]" )!=TCL_OK) break ;
       if (tcl.Eval( "set list [ glob -directory $archive -nocomplain *.* ]" )!=TCL_OK) break ;
      Tcl_Obj list=tcl.Result();
      tcl.Ref(list);
       int total=tcl.Count(list);
       // создадим новую процедуру 
       if (tcl.Eval( "proc content { name } { set f [ open $name rb ] ; set ret [ read $f ] ; close $f ; set ret }" )!=TCL_OK) break ;
       for ( int i= 0 ;i<total;i++) {
         string fileName=tcl.String(list,i);
         tcl.Set( "fileName" ,tcl.Obj(fileName));    
         PrintFormat ( "%s" ,fileName);
         if (tcl.Eval( "set content [ content $fileName ]" )!=TCL_OK) {
             PrintFormat ( "Не удалось прочесть файл %s:%s" ,fileName,tcl.StringResult());
             continue ;
         }
         string sha256=tcl.StringEval( "sha2::sha256 -hex -- $content" );
         PrintFormat ( "  sha256: %s" ,sha256);
         string md5=tcl.StringEval( "md5::md5 -hex -- $content" );
         PrintFormat ( "  md5: %s" ,md5);
      }
      tcl.Unref(list);
      tcl.Eval( "vfs::zip::Unmount $z $archive" );
      ok= true ;
   } while ( false );
   if (!ok) {
       PrintFormat ( "Что-то пошло не так : %s" ,tcl.StringResult());
   }
   delete tcl;
   ATcl_OnDeinit();   
}


Dosyalar:
 

Yeni başlayanlar için elbette bir ekran görüntüsü :-) Bu, 60 mql satır + 60 tcl satırda bir tcp-sunucu danışmanıdır.


Ve bu EA'nın MQ4'ü:

 #property copyright "Maxim A.Kuznetsov"
#property link        "luxtrade.tk"
#property version    "1.00"
#property strict

//--- input parameters
input ushort    PORT= 8000 ;

#include "ATcl.mqh"
ATcl *tcl= NULL ;   // интерпретатор
Tcl_Obj StartServer= 0 ,StopServer= 0 ,SendMsg= 0 ,SymName= 0 ,PortNum= 0 ; // объекты Tcl - имена команд и имя символа
int OnInit ()
{
   ATcl_OnInit(); // включить ATcl
   tcl= new ATcl;   // создать интерпретатор
   if (tcl== NULL || !tcl.Ready()) {
       return INIT_FAILED ;
   }
   // прочтём исходник c командами
   if (tcl.Eval( "source ./MQL4/Experts/atcl_tcpserv.tcl" )!=TCL_OK) {
       PrintFormat ( "Error in Source:%s" ,tcl.StringResult());
       return INIT_FAILED ;
   }
   StartServer=tcl.Ref(tcl.Obj( "StartServer" )); // команда запуска сервера
   StopServer=tcl.Ref(tcl.Obj( "StopServer" ));   // остановка сервера
   SendMsg=tcl.Ref(tcl.Obj( "SendMsg" ));         // рассылка сообщения всем клиентам
   SymName=tcl.Ref(tcl.Obj( Symbol ()));           // запомним имя текущего символа
   PortNum=tcl.Ref(tcl.Obj(( long )PORT));         // и номер порта
   /// запускаем сервер
   if (tcl.Call(StartServer,PortNum)!=TCL_OK) {
       PrintFormat ( "Error on StartServer:%s" ,tcl.StringResult());
       return INIT_FAILED ;
   }
   EventSetTimer ( 2 );
   Print ( "Server started" );
   return ( INIT_SUCCEEDED );
}
void OnDeinit ( const int reason) 
{
   if (tcl!= NULL ) {
       if (tcl.Ready()) {
         // остановить сервер
         tcl.Call(StopServer);
      }
       // освободить все объекты
      tcl.Unref(StartServer);
      tcl.Unref(StopServer);
      tcl.Unref(SendMsg);
      tcl.Unref(SymName);
      tcl.Unref(PortNum);
       // удалить интерпретатор
       delete tcl;
   }
   ATcl_OnDeinit(reason);
}
void OnTick ()
{
   MqlTick tick;
   tcl.Update();
   if ( SymbolInfoTick ( Symbol (),tick)!= 0 ) {
      Tcl_Obj message=tcl.Obj();
      tcl.Ref(message);
      tcl.Append(message,SymName);
      tcl.Append(message,tcl.Obj(( long )tick.time));
      tcl.Append(message,tcl.Obj(( long )tick.time_msc));
      tcl.Append(message,tcl.Obj(( double )tick.bid));
      tcl.Append(message,tcl.Obj(( double )tick.ask));
      tcl.Append(message,tcl.Obj(( long )tick.volume));
       if (tcl.Call(SendMsg,message)!=TCL_OK) {
         PrintFormat ( "Error in SendMsg:%s" ,tcl.StringResult());
      }
      tcl.Unref(message);
   }
}
void OnTimer () {
   tcl.Update();
}
void OnChartEvent ( const int id,         // идентификатор события   
                   const long & lparam,   // параметр события типа long 
                   const double & dparam, // параметр события типа double 
                   const string & sparam   // параметр события типа string
   )
{
   tcl.Update();
}

Şimdiye kadar her şey (kütüphane ve tercüman) Uzman Danışmanlarda veya komut dosyalarında çalışır. Henüz göstergeler için hazır değil :-( OnInit/OnDeinit kuyrukları, DLL yükleme ve boşaltma ve zamanlayıcılar içeren bir tür oyun var.
Dosyalar:
atcl.zip  9 kb
 

Her şey gitgide daha stabil hale geliyor, EventLoop Expert Advisors ve scriptlerde güvenle çalışıyor (ancak göstergelerde hala çalışmıyor)

Yeni, kullanışlı demo - Eşzamansız HTTP istemcisi ve ayrıştırıcısı. Kaynak kodlarının (mq4 ve tcl) yaklaşık 60 satır uzunluğunda olması zaten gelenekseldir.
İstek, uzmana paralel olarak yürütülür ve html sayfasının ayrıştırılması, DOM ayrıştırıcısı aracılığıyla doğru bir şekilde gerçekleştirilir.


 input int       EVERY_MINUTS= 15 ;

#include "ATcl.mqh"
ATcl *tcl= NULL ;
Tcl_Obj StartClient= 0 ,StopClient= 0 ,report= 0 ,minutes= 0 ;
int OnInit ()
{
   ATcl_OnInit(); // включить ATcl
   tcl= new ATcl;   // создать интерпретатор
   if (tcl== NULL || !tcl.Ready()) {
       return INIT_FAILED ;
   }
   // прочтём исходник c командами
   if (tcl.Eval( "source ./MQL4/Experts/atcl_httpclient.tcl" )!=TCL_OK) {
       PrintFormat ( "Error in Source:%s" ,tcl.StringResult());
       return INIT_FAILED ;
   }
   StartClient=tcl.Ref(tcl.Obj( "StartClient" ));
   StopClient=tcl.Ref(tcl.Obj( "StopClient" ));
   report=tcl.Ref(tcl.Obj( "report" ));
   minutes=tcl.Ref(tcl.Obj(( long )EVERY_MINUTS));
   if (tcl.Call(StartClient,minutes)!=TCL_OK) {
       Print ( "call failed" );
       return INIT_FAILED ;
   }
   EventSetTimer ( 1 );
   return ( INIT_SUCCEEDED );
}
void OnDeinit ( const int reason) 
{
   if (tcl!= NULL ) {
       if (tcl.Ready()) {
         tcl.Call(StopClient);
      }
      tcl.Unref(StartClient);
      tcl.Unref(StopClient);
      tcl.Unref(report);
      tcl.Unref(minutes);
       delete tcl;
   }
   ATcl_OnDeinit(reason);
}
void OnTimer ()
{
   tcl.Update();
   if (tcl.IsSet(report)) {
       string comment=tcl.String(tcl.Get(report));
      tcl.Unset(report);
       Comment (comment);
   }   
}

void OnTick () {
   tcl.Update();
}
Neredeyse memnun. Biraz daha fazla ve ticaretle doğrudan ilgili olmayan her şeyi MQL zanaatlarınızdan çıkarmak ve dış dünya ile normal iletişim (veritabanları, mektuplar, raporlar ve hatta bir GUI) eklemek mümkün olacaktır.

Göstergeler ve zamanlayıcılarla ilgili mevcut sorun olmasaydı :-(
Dosyalar:
 

Buraya birden fazla zip dosyası ekleyemiyor musunuz?
veya aynı ada sahip dosyalarla ilgili bir sorun ..

Hâlâ atcl'ın güncel sürümünü EKLEMİYORUM :-) Ya site buggy ya da başka bir şey .. şimdilik proje sayfasından alabilirsiniz.
aksaklıklar geçer buraya eklerim

 
Maxim Kuznetsov :
Biraz daha fazla ve ticaretle doğrudan ilgili olmayan her şeyi MQL zanaatlarınızdan çıkarmak ve dış dünya ile normal iletişim (veritabanları, mektuplar, raporlar ve hatta bir GUI) eklemek mümkün olacaktır.

Ve borulardan dış dünyaya çıkmanızı engelleyen nedir?
sonuç aynı: µl'yi neredeyse unutabilirsiniz - sunucu bir kez yazdı ve hepsi bu.

 
Alexey Oreshkin :

Ve borulardan dış dünyaya çıkmanızı engelleyen nedir?
sonuç aynı: µl'yi neredeyse unutabilirsiniz - sunucu bir kez yazdı ve hepsi bu.

ve boruları kim kontrol edecek, protokolü uygulayacak ve sonuçları ayrıştıracak? uzun, yavaş ve sadece anketler.

ve burada tamamen farklı bir entegrasyon seviyesi var - tcl ve mql arasında kolayca veri aktarabilirsiniz - bunlar aynı bellekte, tek bir işlemde oturuyorlar. Teorik olarak (işi kontrol etmedim, ancak çalışması gerekir) ve biraz doğrulukla değişkenleri bağlayabilirsiniz.
tcl'de olan ancak mql'de eksik olan tüm özelliklerin ve kitaplıkların kolayca erişilebilir olduğu ortaya çıktı.
 

GÖSTERGELER (neredeyse) :-) Yani, ATcl artık göstergelerde çalışıyor, talihsiz EventLoop dahil.

Ekranda - önceki programla aynı program (eşzamansız http istemcisi), ancak şimdi göstergede:


Tabii ki, DLL başlatma algoritmasının aynı olduğu ortaya çıktı :-)

API-Önizleme neredeyse bitti diyebiliriz, sadece kaynak kodunu belgelerle düzeltin ve Beta sürümünü duyurmak mümkün olacak.

Hindiyi ve kütüphaneyi ekliyorum. Ve bunu birayla kutlayacağım :-)

Dosyalar: