如何从MQL5MQL4访问MySQL数据库_第1页
如何从MQL5MQL4访问MySQL数据库_第2页
如何从MQL5MQL4访问MySQL数据库_第3页
如何从MQL5MQL4访问MySQL数据库_第4页
如何从MQL5MQL4访问MySQL数据库_第5页
已阅读5页,还剩11页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、如何从 MQL5 (MQL4) 访问 MySQL 数据库介绍MQL 与数据库的交互问题并非新事物,但它们依然是相关的。利用数据库可以极大增强 MetaTrader 的可塑性: 存储并分析价格历史,从一个交易平台拷贝交易至另一个平台,提供实时报价/交易,在服务器端定期进行深度分析计算,使用 web 技术监视并远程控制账户。总之,有许多种应用尝试从 MQL 和 MySQL 的组合之中获益,一些方案已经在代码库里出现。例如 "MySQL 包装 - 用于 MetaTrader 4 的链接库" 就是这样的项目,许多程序员开始自己开发,在将来还可扩充。我认为,这种解

2、决方案的缺点之一是分配特殊数组用来从数据库中读数据。另一个项目 "MySQL 日志 1 - 用于 MetaTrader 4 的 EA" 更加专业,它不使用包装来访问标准链接库 libmysql.dll。因此,它不能在 MetaTrader4 编译版 600+ 上工作,由于 char 字符类型已经被 wchar_t 替代,且使用 int 类型替代了 TMYSQL 结构指针,导致在项目中产生内存泄漏 (内存分配不能控制/释放)。另一个有趣的项目是 "EAX_M

3、ysql - MySQL 链接库 - 用于 MetaTrader 5 的链接库"。它是十分出色的实现。不过作者列出了一些缺点,在使用时有强制限制。任何人若需要在他们的项目中使用数据库,有两个选项: 要么开发自己的解决方案,并了解它的每一个部分,或者使用/改编任何第三方解决方案,了解如何使用它们并检测是否会阻碍他们的项目。在我开发一个相当复杂的自动交易时,就要面对这样的必要性和两个选项。依照现有项目经过搜索,且研究了很多的解决方案后,我意识到,已发现的实施方案均无助于把我的自动交易提升到“专业水平”。此外,也有些荒谬的方案,例如: 使用标准 libmysql.dll 执行 DML/DD

4、L 操作 (插入/更新/删除数据, 在数据库中创建/废弃对象), 以及将数据检索 (SELECT) 的实现作为 HTTP 请求 (使用 inet.dll) 与 MySQL 服务器端的 web 服务器上的 PHP 脚本通信。而 SQL 查询被写在 PHP 脚本中。换句话说,要运行该项目,一定需要保证下述所有部件准备妥当,配置好并运行:MySQL 服务器,Apache/ IIS Web 服务器,在服务器端的 PHP/ASP 脚本. 大量技术的组合。当然,在某些情况下,这是可以接受的,但当唯一的任务就是从数据库中查询数据 - 那么这些全无意义。此外,支持如此累赘的方案也耗费时间。大部分的方案在插入数

5、据,创建对象等等操作时没有问题。问题在于数据查询,因为数据将会被返回调用环境。我认为出于此目的而使用数组是不切实际的和不方便的,简单的原因就是在主程序的开发/调试/支持过程中,数据库查询是可以变化的,而您也要正确控制为数组分配的内存. 那么,这些可以,而且必须要避免。下文讨论的 MQL <-> MySql 的接口基于 Oracle PL/SQL, MS SQL T-SQL, AdoDB 等产品内使用的典型方式- 使用游标。这个接口的开发目标是易于编程和维护,再加上最少元部件。它作为 DLL 包装器实现,连接标准链接库 libmysql.dll,且接口函数集合作为一个 .mqh 文件

6、。1. MQL <-> MySQL 接口在 MetaTrader 终端之间交互 (通过 MQL 程序) 可以在如下元部件的帮助下实现:1. 接口库 MQLMySQL.mqh. 使用 #include 语句将它加到项目工程里,并且可以按照您的喜好进行修改。它包含的指令用于导入 MQLMySQL.dll 动态库的函数,以及调用它们和处理错误的函数。2. MQLMySQL.dll 动态库。这是一个包装器,用来访问标准库 libmysql.dll 的功能。此外,MQLMySQL.dll 链接库处理操作的结果并共享访问数据库的连接和游标。这意味着您可以在同一时间创建和使用

7、多个连接 (来自一个或多个 MQL 程序), 保持少量的打开游标, 查询一个或多个数据库。互斥则用于分隔访问共享资源。3. 标准动态链接库 libmysql.dll 是本地访问驱动器。您可以从任何 MySql 数据库的发布位置 C:WindowsSytem32 或 <终端>MQL5Libraries (对于 MetaTrader 4 在 <终端>MQL4Libraries) 中拷贝它。事实上,它负责发送查询到数据库并接收检索结果。让我们来详述要点,诸如: 打开/关闭连接, 执行 DML/DDL 查询和数据检索。1.1. 打开和关闭连接该 MySqlConnect 函数已

8、经实现了打开与 MySQL 数据库的连接:类型名称参数描述intMySqlConnect该功能已实现与数据库的连接并返回一个连接标识符。这个 ID 在数据库查询时需要。在连接失败情况下,则返回值 "-1"。对于错误详情,检查变量 MySQLErrorNumber 和 MySqlErrorDescription。典型地,这个函数在 MQL 程序处理 OnInit() 事件时调用。string pHost这是 MySQL 服务器的域名或 IP 地址string pUser数据库用户名 (例如, root)st

9、ring pPassword数据库用户的口令string pDatabase数据库名称int pPort数据访问的 TCP/IP 端口 (通常是 3306)string pSocketUnix 套接 (对于 Unix 基准的系统)int pClientFlag特殊标志组合 (通常是 0)该 MySqlDisconnect 接口函数已实现关闭连接:类型名称参数描述voidMySqlDisconnect此函数关闭与 MySQL 数据库的连接。典型地,这个函数在 MQL 程序处理 OnDeinit() 事件时调用。int 

10、;pConnection连接标识符应当注意的是,MySQL 数据库在硬件故障时,网络拥塞或超时 (若长时间无查询发送到数据库),能够自行关闭连接。开发者经常利用 OnTick() 事件写数据至数据库。然而,当周末来临,市场闭市,连接仍然在“悬挂”。在此情况下, MySQL 将超时关闭 (省缺是 8 小时)。而周一,当市场开市,系统发现连接错误。因此,强烈建议每隔一段时间就检查连接,并且/或者重新连接服务器,时间间隔应稍小于 MySQL 服务器设置中指定的超时数值。1.2. 执行 DML/DDL 查询DML 操作用于数据操纵 (数据操纵语言)。数据操纵包括以下语句集合: IN

11、SERT, UPDATE 和 DELETE。DDL 操作用于数据定义 (数据定义语言)。这些包括创建 (CREATE) 数据库对象 (表, 视图, 存储过程, 触发器, 等等) 以及修改 (ALTER) 和删除 (DROP)。这些不是全部的 DML/DDL 语句, 此外, DCL (数据控制语言) 用于分隔数据访问, 但我们不会深入探讨 SQL 的特性。所有这些命令都可以使用 MySqlExecute 接口函数执行:类型名称参数描述boolMySqlExecute这个函数用来在数据库成功建立之后 (使用 MySqlConnect 函数),执行非 SELECT 的 SQL 语

12、句。如果命令执行成功,函数返回 true,否则 - false。有关错误详情, 使用 MySQLErrorNumber 和MySqlErrorD pConnection连接标识符string pQuerySQL 查询作为一个 SQL 查询,您也可以使用 USE 命令选择数据库。我想提醒一下使用复合语句的查询。它是 SQL 命令集合,分隔字符 ""。要启用复合语句模式,打开数据库连接时应携带 CLIENT_MULTI_STATEMENTS 标志:.int ClientFlag = CLIENT_MULTI_S

13、TATEMENTS; / Setting the multi-statements flagint DB; DB = MySqlConnect(Host, User, Password, Database, Port, Socket, ClientFlag); / Connection to the databaseif (DB = -1)       / Handling the connection error   ./ Preparing a SQL query to insert data (3 rows

14、in one query)string SQL;SQL = "INSERT INTO EURUSD(Ask,Bid) VALUES (1.3601,1.3632);"SQL = SQL + "INSERT INTO EURUSD(Ask,Bid) VALUES (1.3621,1.3643);"SQL = SQL + "INSERT INTO EURUSD(Ask,Bid) VALUES (1.3605,1.3629);".if (!MySqlExecute(DB,SQL)       

15、; / Showing an error message   .在这个片段中,利用单次调用数据库,3 个条目将被插入 EURUSD 表中。存储在 SQL 变量中的每个查询,通过 "" 分隔。这种方法可用来频繁插入/更新/删除;必要的命令集合组合到一个 "包" 中,从而减轻了网络流量,并提高数据库的性能。在 MySQL 中,INSERT 语法对异常处理十分出色。例如,如果任务是移动价格历史,应该创建一个对应货币对的表,其主键是日期型,因为柱线的日期和时间都是唯一的。此外,应该检查是否任意特定的柱线数据都存在于数据库中 (提高数

16、据迁移的稳定性)。对于 MySQL 不需要这项检查, 因为 INSERT 语句支持 ON DUPLICATE KEY。简单来说, 如果尝试插入数据, 且表中已经有一个记录带有相同日期和时间, 则 INSERT 语句可以被忽视,或此行被 UPDATE 替换 (参见)。1.3. 数据检索SQL SELECT 语句用于从数据库中检索数据。下面的操作序列用于检索数据并返回检索的结果:1. 准备 SELECT 语句。2. 打开游标。3. 获得查询回的行数。4. 循环获取结果的每一行。5. 在循环内将数据赋值给 MQL 变量。6. 关闭游标。当然,这是常用规划,所以并非每种情况都需要全部操作。例如,如果您

17、打算确认表中存在一行数据 (按照任意标准), 这对于准备查询足够了, 打开游标, 得到行数并关闭游标。事实上,强制部分是 - 准备 SELECT 语句,打开和关闭游标。什么是游标?它是针对一片具有逻辑关系的内存区域的引用, 实际上 - 结果数值的集合。当您发送 SELECT 查询, 数据库为结果分配内存并创建行指针,您可以利用其在数据行之间移动。因此,可以按顺序访问按照查询定义的队列的每一行。 (SELECT 语句的 ORDER BY 子句)。以下接口函数用于数据检索:打开游标:类型名称参数描述intMySqlCursorOpen这个函数为 SELECT 查询打开一个游标,若成功则返回游标标识

18、符。否则, 函数返回 "-1"。为了查找错误原因, 使用变量 MySQLErrorNumber 和MySqlErrorD pConnection数据库连接标识符string pQuerySQL 查询 (SELECT 语句)获得查询返回的行数:类型名称参数描述intMySqlCursorRows这个函数返回查询的检索行数。int pCursorID由 MySqlCursorOpen 返回的游标标识符取查询数据行:类型名称参数描述boolMySqlCursorFetchRow从查询

19、返回的数据集合中取一行数据。在成功执行之后, 您可以将数据赋值到 MQL 变量。若成功函数返回 true, 否则返回  pCursorID由 MySqlCursorOpen 返回的游标标识符取查询数据行之后,赋值到 MQL 变量:类型名称参数描述intMySqlGetFieldAsInt函数以 int 数据类型返回数据表字段的值。int pCursorID由 MySqlCursorOpen 返回的游标标识符int pField在 SELECT 中的字段号码 (起始编号为 0)double

20、MySqlGetFieldAsDouble函数以 double 数据类型返回数据表字段的值。int pCursorID由 MySqlCursorOpen 返回的游标标识符int pField在 SELECT 中的字段号码 (起始编号为 0)datetimeMySqlGetFieldAsDatetime函数以 datetime 数据类型返回数据表字段的值。int pCursorID由 MySqlCursorOpen 返回的游标标识符int pField在 SELECT 中的字段

21、号码 (起始编号为 0)stringMySqlGetFieldAsString函数以 string 数据类型返回数据表字段的值。int pCursorID由 MySqlCursorOpen 返回的游标标识符int pField在 SELECT 中的字段号码 (起始编号为 0)所有 MySQL 返回的数据都有本地表达方式 (不仅表达为字符串)。因此,使用这些函数,您可以将所选的数据转换为所需的类型。唯一不足的是,在 SELECT 清单中,用列编号(起始编号为 0)替代了它的名字。不过,当开发一个应用程序时,准备 SELECT 语句并获取

22、结果几乎都在一个页面,所以当您规定数据获取逻辑时,您可以看到 SELECT 查询。因此,您总能知道在 SELECT 清单中的字段数量 (这种方式也同样适用于访问 AdoDB 的数据)。好了,这部分可以在以后修改。它对方案中的功能开发只是略有影响。关闭游标:类型名称参数描述voidMySqlCursorClose这个函数关闭指定的游标并释放内存。int pCursorID由 MySqlCursorOpen 返回的游标标识符关闭游标是一个关键操作。 不要忘记关闭游标。想象一下您打开了一个游标,并忘记关闭它。设想,每次即时报价到达,处理 OnTick

23、() 事件时,数据都要通过游标检索,并且每次都打开一个新游标,并为其分配内存 (客户端与服务器端两者相同)。在某一时刻,服务器将拒绝服务,因为到达打开游标限制,并且导致缓存区溢出。当然,这有点夸张,这样的结果可能在与 libmysql.dll 直接工作的时候出现。不过,MQLMySQL.DLL 动态链接库为游标分配内存,并将在超出了允许的限制时拒绝打开新游标。当执行实际任务时,保持 2-3 个打开游标就足够了。每个游标可以处理一个笛卡尔尺寸的数据; 并发使用二至三个游标 (嵌套,例如,一个参数依赖于另一个游标) 可覆盖二至三个维度。对于大多数任务这是完全正常的。此外,为了实现复杂的数

24、据检索,您可以随时使用这些对象来代表数据库 (视图),在服务器端创建它们,并从 MQL 代码里发送查询,就如同数据表一样。1.4. 附加信息下面提到的可以作为附加特征:1.4.1. 从一个 .INI 文件中读取数据类型名称参数描述StringReadIni返回 INI 文件中给定段落的键值。string pFileNameINI 文件名filestring pSection段落名string pKey键名经常在 MQL 代码(或在 EA、指标、脚本的参数)中直接保存有关数据库连接的信息(服务器的 IP 地址, 端口, 用户名, 口令, 等等)是不理性的,因为服务器也许会转移,它的地址也会动态变

25、化,等等。在这种情况下您将需要修改 MQL 代码。因此,所有这些数据应该最好被存储在标准 .INI 文件里,而只将它的名称写在 MQL 程序中。然后,使用 ReadINI 函数来读取连接参数,并使用它们。例如,INI 文件包含以下信息:MYSQLServer = User = rootPassword = Adm1n1strt0rDatabase = mysqlPort = 3306为了得到服务器 IP 地址,执行以下语句:string vServer = ReadIni("C:MetaTrader5MQL5ExpertsMyConnection.ini"

26、, "MYSQL", "Server");该 INI 文件被放置于 C:MetaTrader5MQL5Experts 并且称为 "MyConnection.ini",您可以访问 MYSQL 段落的 Server 键值。在一个 INI 文件中您可以保存多个您的项目中用到的服务器设置。1.4.2. 问题区域追踪在接口库中提供了追踪模式,可以启用该模式来调试 MQL 程序中任意位置的 SQL 查询。在问题区域指定如下:SQLTrace = true;或是SQLTrace = false;如果您在 MQ

27、L 程序开始启用追踪,并且未禁用它,则所有数据库调用都将被记录。而记录被保持在终端的控制台里 (使用 Print 命令)。2. 例程这一段提供了一些连接和使用已开发的链接库的例程。参阅它们并评估软件解决方案的可用性。例程 MySQL-003.mq5 出示如下: 连接数据库 (连接参数保存在 .ini 文件), 创建数据表, 插入数据 (也用到复合语句) 以及从数据库断开连接。/+-+/|                

28、;                                    MySQL-003.mq5 |/|           

29、                        Copyright 2014, Eugene Lugovoy |/|                     &

30、#160;                         |/| Inserting data with multi-statement (DEMO)                 &#

31、160;     |/+-+#property copyright "Copyright 2014, Eugene Lugovoy."#property link      ""#property version   "1.00"#property strict#include <MQLMySQL.mqh>string INI;/+-+/| Script program start function

32、60;                                   |/+-+void OnStart() string Host, User, Password, Database, Socket; / database credentia

33、ls int Port,ClientFlag; int DB; / database identifier Print (MySqlVersion(); INI = TerminalInfoString(TERMINAL_PATH)+"MQL5ScriptsMyConnection.ini" / reading database credentials from INI file Host = ReadIni(INI, "MYSQL", "Host"); User = ReadIni(INI, "MYSQL", &

34、quot;User"); Password = ReadIni(INI, "MYSQL", "Password"); Database = ReadIni(INI, "MYSQL", "Database"); Port     = (int)StringToInteger(ReadIni(INI, "MYSQL", "Port"); Socket   = ReadIni(INI, "MYSQL&q

35、uot;, "Socket"); ClientFlag = CLIENT_MULTI_STATEMENTS; /(int)StringToInteger(ReadIni(INI, "MYSQL", "ClientFlag");   Print ("Host: ",Host, ", User: ", User, ", Database: ",Database); / open database connection Print ("Connecti

36、ng."); DB = MySqlConnect(Host, User, Password, Database, Port, Socket, ClientFlag); if (DB = -1) Print ("Connection failed!Error: "+MySqlErrorDescription); else Print ("Connected!DBID#",DB); string Query; Query = "DROP TABLE IF EXISTS test_table" MySqlExecute(DB, Q

37、uery); Query = "CREATE TABLE test_table (id int, code varchar(50), start_date datetime)" if (MySqlExecute(DB, Query)         Print ("Table test_table created.");          / Inserting data 1 row &

38、#160;   Query = "INSERT INTO test_table (id, code, start_date) VALUES ("+(string)AccountInfoInteger(ACCOUNT_LOGIN)+",'ACCOUNT','"+TimeToString(TimeLocal(), TIME_DATE|TIME_SECONDS)+"')"     if (MySqlExecute(DB, Query) &#

39、160;               Print ("Succeeded: ", Query);             else                

40、; Print ("Error: ", MySqlErrorDescription);         Print ("Query: ", Query);                  / multi-insert     Query =   &

41、#160;     "INSERT INTO test_table (id, code, start_date) VALUES (1,'EURUSD','2014.01.01 00:00:01');"     Query = Query + "INSERT INTO test_table (id, code, start_date) VALUES (2,'EURJPY','2014.01.02 00:02:00');&qu

42、ot;     Query = Query + "INSERT INTO test_table (id, code, start_date) VALUES (3,'USDJPY','2014.01.03 03:00:00');"     if (MySqlExecute(DB, Query)              

43、0;  Print ("Succeeded!3 rows has been inserted by one query.");             else                 Print ("Error of multiple statements: "

44、;, MySqlErrorDescription);             else         Print ("Table test_table cannot be created. Error: ", MySqlErrorDescription);     MySqlDisconnect(DB); Print ("Di

45、sconnected. Script done!");例程 MySQL-004.mq5 出示从数据表中检索数据,该表通过 "MySQL-003.mq5" 脚本创建。 /+-+/|                              &

46、#160;                     MySQL-004.mq5 |/|                         &#

47、160;         Copyright 2014, Eugene Lugovoy |/|                                   

48、60;           |/| Select data from table (DEMO)                                  

49、;  |/+-+#property copyright "Copyright 2014, Eugene Lugovoy."#property link      ""#property version   "1.00"#property strict#include <MQLMySQL.mqh>string INI;/+-+/| Script program start function   &

50、#160;                                |/+-+void OnStart() string Host, User, Password, Database, Socket; / database credentials int Port,Clie

51、ntFlag; int DB; / database identifier Print (MySqlVersion(); INI = TerminalInfoString(TERMINAL_PATH)+"MQL5ScriptsMyConnection.ini" / reading database credentials from INI file Host = ReadIni(INI, "MYSQL", "Host"); User = ReadIni(INI, "MYSQL", "User")

52、; Password = ReadIni(INI, "MYSQL", "Password"); Database = ReadIni(INI, "MYSQL", "Database"); Port     = (int)StringToInteger(ReadIni(INI, "MYSQL", "Port"); Socket   = ReadIni(INI, "MYSQL", "Sock

53、et"); ClientFlag = (int)StringToInteger(ReadIni(INI, "MYSQL", "ClientFlag");   Print ("Host: ",Host, ", User: ", User, ", Database: ",Database); / open database connection Print ("Connecting."); DB = MySqlConnect(Host, User,

54、Password, Database, Port, Socket, ClientFlag); if (DB = -1) Print ("Connection failed!Error: "+MySqlErrorDescription); return; else Print ("Connected!DBID#",DB); / executing SELECT statement string Query; int    i,Cursor,Rows; int      vId; string   vCode; datetime vStartTime; Query = "SELECT id, code, start_date FROM test_table" Print ("SQL> ", Query); Cursor = MySqlCursorOpen(DB, Query); i

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论