有趣的东西。
我像往常一样,对R的想法念念不忘。在我看来,你是一个合适的人,至少可以和你讨论这个想法。
该想法如下。
今天,我们可以把一个由mcl4/5编译器生成的exesh文件拖到终端图表中。
有没有可能做一个开发,能够将R脚本链到图表上?
有趣的东西。
我像往常一样,对R的想法 念念不忘。在我看来,你是一个合适的人,至少可以和你讨论这个想法。
该想法如下。
今天,我们可以把一个由mcl4/5编译器生成的exesh文件拖到终端图表中。
还有,有没有可能做一个开发,能够将R脚本链到图表上?
顺便说一下,关于R :-)你是否注意到了R分配套件?
嗯,有一个library/tcltk的目录,里面有内置的R tcl。当一种语言/平台缺乏设施时,他们会在其中加入tcl,又称 "工具通用语言"。R缺乏GUI,并将tcl/tk放入其中(就像Python和Ruby中一样)。
我在MQL中缺乏许多功能,除了图表,所以我得到了ATcl。
ATcl也可以进行统计 :-)另一个小测试案例--计算一个数据集的基本统计值。脚本采用Close,与将在输入参数中给出的一样多。
#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(); }
![](https://c.mql5.com/3/168/2018-01-15_16-01-02__1.png)
附带的是脚本。只有一条计算线 :-)
还有一个例子:-)
让我们拿一个压缩包来计算存储在其中的文件的校验和。我们计算 MD5 和 SHA256
#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(); }
只是作为开始,当然要有截图 :-)这是一个由60行mql+60行tcl组成的EA tcp-server。
还有专家顾问本身的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(); }到目前为止,一切(库和解释器)都能在EA或脚本中工作。对于指标来说,它还没有准备好 :-( 在OnInit/OnDeinit序列、DLL加载和定时器方面存在某种奇怪的现象。
一切都变得越来越稳定,EventLoop可以在EA和脚本中自信地工作(但在指标中仍不例外)。
新的、有用的演示--异步HTTP-客户端和解析器。源代码(mq4和tcl)大约有60行。
查询与专家顾问平行执行,通过DOM解析器正确地进行了HTML解析。
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(); }我几乎满意了。用不了多久,我就可以从我的MQL假货中删除一切与交易没有直接关系的东西,并增加与外界的正常通信(数据库、电子邮件、报告甚至GUI)。
如果不是因为目前的指标和计时器的问题 :-(。
再过一段时间,你可以从你的MQL创作中删除一切与交易没有直接关系的东西,并增加与外界的正常通信(数据库、电子邮件、报告甚至GUI)。
又是什么阻止了你通过管道去外面的世界?
结果是一样的:你几乎可以忘记mql--写一次服务器,就可以了。
是什么阻碍了你通过管道进入外部世界?
结果是一样的:你几乎可以忘记µl--写一次服务器,就可以了。
而且还有一个完全不同层次的整合--数据可以很容易地在tcl和mql之间传输--它们就在一个内存中,在一个进程中。在理论上(没有检查工作,但应该可以),而且有一定的准确性,你可以把变量联系起来。
事实证明,所有在tcl中存在但在mql中缺乏的功能和库都很容易获得。
指示器被打败了(几乎是):-)也就是说,ATcl现在可以在指标中工作,包括命运不佳的EventLoop。
在截图中--与上次相同的程序(异步http客户端),但现在是指标。
当然,DLL的初始化算法是不一样的 :-)
我们可以说,API-预览版已基本完成,只需清理源代码和文档,然后我们就可以宣布测试版了。
附上火鸡和图书馆。而我要用啤酒来庆祝 :-)
假期收获颇丰,我很高兴向大家介绍ATcl - MT4的内置Tcl解释器。
这个项目的想法是(现在也是),在不直接移植Tcl C Api函数的情况下,为解释器提供最方便的接口。
这就是我得到的结果(工作实例--一个将报价保存到SQLite数据库的脚本)。
在这个阶段,它是一个休眠的阿尔法或API预览 - 评估API的可用性。但是,正如你所看到的,DBMS的接口可以工作,在一定程度上EventLoop也可以工作,也就是说,你可以使用异步命令、I/O和定时器。
我附上了当前版本的档案,你可以试试...
文档,我正在尽可能地更新,最新的版本可以在项目页面上找到http://luxtrade.tk/atcl。
在这个主题中,我准备回答关于Tcl/Tk本身和提到的ATcl库的问题。欢迎大家提出想法、意见、建议和批评。
更新:在本主题中附上脚本的源代码
更新:更新Atcl.zip