文章 "开发多币种 EA 交易 (第 13 部分):自动化第二阶段 — 分组选择"

 

新文章 开发多币种 EA 交易 (第 13 部分):自动化第二阶段 — 分组选择已发布:

我们已经实现了自动化优化的第一阶段。我们根据若干标准对不同的交易品种和时间框架进行优化,并将每次通过的结果信息存储在数据库中。现在我们将从第一阶段找到的参数集中选择最佳组。

下一阶段是选择一组优秀的单个交易策略实例,当它们协同工作时,将改善交易参数 — 减少回撤、增加余额曲线增长的线性等等。我们已经在本系列文章的第六部分中研究了如何手动执行此阶段。首先,我们从优化单个交易策略实例参数的结果中选择了值得关注的。这可以使用各种标准来完成,但当时我们只限于简单地删除负利润的结果。然后,我们使用不同的方法,尝试将八个交易策略实例的不同组合,将它们组合在一个 EA 中,并在测试器中运行它们,以评估它们共同工作的参数。

从手动选择开始,我们还实现了从存储在 CSV 文件中的参数列表中选择的单一交易策略实例的输入参数组合的自动选择。事实证明,即使在最简单的情况下,当我们简单地运行选择八种组合的遗传优化时,也能实现预期的结果。

现在,让我们修改执行组选择优化的 EA,以便它可以使用数据库中第一阶段的结果。它还应将结果保存在数据库中。我们还将考虑通过向数据库添加必要的条目来创建进行第二阶段优化的任务。

作者:Yuriy Bykov

 
非常感谢你这篇有趣的文章!关于static 修饰符,我理解您的处境,起初我也认为借助该修饰符,您可以像在 pluses 上一样创建自己的单例,以访问内存的相同部分。但实践证明,该修改器只适用于运行 Expert Advisor 本身的符号。
 
感谢您的精彩文章!
 

感谢您的反馈,我们将继续努力。

亚历山大,我猜你还没有完全弄明白如何使用 static。在它的帮助下,Singleton 设计模式可以很容易地在 MQL5 和 C++ 中实现。例如,我在第三 部分的CVirtualReceiver 类中就使用了它。该修改器与运行智能交易系统的图表没有任何关系。如果我们将 Symbol() 函数调用的结果赋值给变量或属性,使用该修改器声明的变量或属性可能会与之相关。但这并不意味着我们不能事后更改这些变量的值

 
尤里,你好!能告诉我你在哪个程序中打开和编辑数据库吗?我在 mql5 编辑器中打开了数据库,但在那里无法进行编辑(按钮和菜单项都是灰色的),而且 sem 界面看起来有点不同?
 

你好,维克多。

我使用SQLiteStudio。这个免费程序最近扩展了很多功能,所以我还没有遇到缺少必要功能的情况。在 MetaEditor 中,您只能通过执行 SQL 查询来编辑数据库。这当然不太方便。

 

尤里,谢谢你的程序,现在我可以打开数据库并进行编辑了。我已经完成了第一阶段的计算,但第二阶段出现了问题--无法启动。我首先运行了第一阶段,然后手动添加了第二阶段的一行,如截图所示,并执行了您文章 中的两个查询。任务和工作出现在数据库中,Expert Advisor 尝试运行第二阶段。但出于某种原因,它看不到第一阶段的通行证,尽管它们在数据库中。我可能理解错了(我没有使用过任何人的数据库)。

下面是截图上的错误。如何运行?

附加的文件:
37yepqooe5.png  129 kb
7339o8y4fn1.png  21 kb
 

此外,根据我对这些参数的理解

input int      count_         = 16;                   // - 小组中的战略数量(1 ... 16)

input int   i1_ = 1;       // - 战略索引 #1
input int   i2_ = 2;       // - 战略索引 #2
input int   i3_ = 3;       // - 战略索引 #3
input int   i4_ = 4;       // - Strategy Index #4
input int   i5_ = 5;       // - Strategy Index #5
input int   i6_ = 6;       // - Strategy Index #6
input int   i7_ = 7;       // - Strategy Index #7
input int   i8_ = 8;       // - 战略索引 #8
input int   i9_ = 9;       // - Strategy Index #9
input int   i10_ = 10;     // - 战略索引 #10
input int   i12_ = 11;     // - 战略索引 #11
input int   i11_ = 12;     // - 战略索引 #12
input int   i13_ = 13;     // - 战略索引 #13
input int   i14_ = 14;     // - 战略索引 #14
input int   i15_ = 15;     // - 战略索引 #15
input int   i16_ = 16;     // - 战略索引 #16

应该在第二阶段的每个任务中进行搜索。但应该在什么范围内搜索,用什么步骤搜索?我的理解是,不应该搜索 count_ 参数本身?

 

在第二阶段,应自动创建第二个数据库并发送给测试代理。其名称在指令

#define  PARAMS_FILE "database892.stage2.sqlite"

必须与主数据库的名称不同。从截图中可以看出,通行证表并不在该数据库中,尽管它本应在其中。尝试理解 CreateTaskDB() 函数的工作,该函数从初始数据库创建第二个数据库。

Optimisation.mq5 Expert Advisor 会根据第二个数据库的信息自动设置搜索 i{N}_ 类型参数的步骤和限制。

不需要搜索 count_ 参数。如果我们不是要从 16 个实例中选择分组,而是要从更少的实例中选择分组,可以将其改为一个较小的值。例如,从 12 个实例或 8 个实例中选择。但对我来说,它始终等于 16。

 

尤里,我无法让它工作....。我所拥有的普通数据库文件与您在顾问数据库 892.sqlite 中指定的一样,我没有更改过它,而且它确实在光盘上,顾问 Optimisation.mq5 可连接到它并运行任务。在专家顾问 SimpleVolumesStage2.mq5 中也指定了它。据我所知,它们是不同的文件。通用数据库文件位于 Common\Files 文件夹

我尝试在 GetParamsTotal 函数中插入检查,并在 DB::Connect 函数中指定 fileName 变量,以下是代码:

//+------------------------------------------------------------------+
//| 任务数据库中的策略参数集数量
//+------------------------------------------------------------------+
int GetParamsTotal(const string fileName) {
   int paramsTotal = 0;
         PrintFormat(__FUNCTION__" 1 ");

// 如果任务数据库已打开,则
   if(DB::Connect(fileName, 0)) {
         PrintFormat(__FUNCTION__" 2 ");

      // 创建一个查询,以获取该任务的通过次数
      string query = "SELECT COUNT(*) FROM passes p";
         PrintFormat(__FUNCTION__" 3 ");

      int request = DatabasePrepare(DB::Id(), query);
         PrintFormat(__FUNCTION__" 4 ");
      

      if(request != INVALID_HANDLE) {
         // 查询结果的数据结构
         PrintFormat(__FUNCTION__" 5 ");

         struct Row {
            int      total;
         } row;
         PrintFormat(__FUNCTION__" 6 ");
         
         // 从第一行获取查询结果
         if (DatabaseReadBind(request, row)) {
            paramsTotal = row.total;
         }
      } else {
         PrintFormat(__FUNCTION__" | ERROR: request \n%s\nfailed with code %d", query, GetLastError());
      }
      DB::Close();
   }
         PrintFormat(__FUNCTION__" 7 ");

   return paramsTotal;
}

在日志中执行时,输出结果如下:

2024.08.21 22:05:27.964 Optimization (EURUSD,M5)        idTask_=124||0||0||0||N
2024.08.21 22:05:27.964 Optimization (EURUSD,M5)        idParentJob_=7||0||1||10||N
2024.08.21 22:05:27.964 Optimization (EURUSD,M5)        
2024.08.21 22:05:29.096 SimpleVolumesStage2 (GBPUSD,H1) GetParamsTotal 1 
2024.08.21 22:05:29.097 SimpleVolumesStage2 (GBPUSD,H1) GetParamsTotal 2 
2024.08.21 22:05:29.097 SimpleVolumesStage2 (GBPUSD,H1) GetParamsTotal 3 
2024.08.21 22:05:29.097 SimpleVolumesStage2 (GBPUSD,H1) database error, no such table: passes
2024.08.21 22:05:29.097 SimpleVolumesStage2 (GBPUSD,H1) GetParamsTotal 4 
2024.08.21 22:05:29.097 SimpleVolumesStage2 (GBPUSD,H1) GetParamsTotal | ERROR: request 
2024.08.21 22:05:29.097 SimpleVolumesStage2 (GBPUSD,H1) SELECT COUNT(*) FROM passes p
2024.08.21 22:05:29.097 SimpleVolumesStage2 (GBPUSD,H1) failed with code 5039
2024.08.21 22:05:29.098 SimpleVolumesStage2 (GBPUSD,H1) GetParamsTotal 7 
2024.08.21 22:05:29.098 SimpleVolumesStage2 (GBPUSD,H1) OnTesterInit | ERROR: Can't load data from file database892.sqlite.
2024.08.21 22:05:29.098 SimpleVolumesStage2 (GBPUSD,H1) Check that it exists in data folder or in common data folder.
2024.08.21 22:05:32.900 Optimization (EURUSD,M5)        OnTimer | Current Task ID = 124
2024.08.21 22:05:33.008 Optimization (EURUSD,M5)        FinishTask | Task ID = 124
2024.08.21 22:05:33.022 Optimization (EURUSD,M5)        StartTask | Task ID = 125
2024.08.21 22:05:33.022 Optimization (EURUSD,M5)        [Tester]

其中错误编号仍可写为 5602。据我所知,它是在DatabasePrepare 函数中遇到的。

我将您在文件中发布的数据库添加到文章 11 中,唯一做的是将文件名从database.sqlite 更改为database892.sqlite,并将数据库中第一阶段顾问的名称更改为本部分中的实际名称,在执行第一阶段后,我将第二阶段的行添加到阶段表中,并执行了您文章中的 2 组命令。我没有更改任何其他内容。通行证表中大约有 388,000 行通行证。

 

错误发生的时间较早,只是没有报告而已,因为从程序执行 的角度来看一切正常。

这条信息

2024.08.21 22:05:29.097 SimpleVolumesStage2 (GBPUSD,H1) database error, no such table: passes

直接表明第二个数据库中没有传递 表,而我们正试图从中获取数据。这就是为什么我们应该处理应该创建它的函数--CreateTaskDB()