首页 系统交易 帖子

下载MetaTrader 5中的所有符号历史数据

附件
56324.zip (3.28 KB, 下载 1次)

今天我们来聊聊如何使用专家顾问(EA)来下载MetaTrader 5中某个符号的所有历史数据。这段代码会扫描您经纪商的市场观察列表,并提取出可以下载所有可用ticks的符号。

这个工具可以帮助您下载所有符号的历史数据,以便进行回测,或者从这些ticks创建自定义图表。

请注意,终端会将ticks缓存到数据文件夹中,所以确保您的硬盘空间足够。

为了方便下载,我们首先需要一个下载管理器。

以下是 CDownloadManager 结构,它包含我们需要保留的所有信息:

struct CDownloadManager {
   bool m_started, m_finished;
   string m_symbols[], m_current;
   int m_index;
}

它包含了以下信息:

  • 下载状态(已开始/已完成)
  • 待扫描的符号列表
  • 当前符号
  • 正在扫描的符号索引

我们还需要读写硬盘,因为我们在处理符号时,需要创建两个快速的函数来从二进制文件中读写字符串。

保存字符串到文件的函数:

void writeStringToFile(int f, string thestring) {
   // 保存符号字符串
   char sysave[];
   int charstotal = StringToCharArray(thestring, sysave, 0, StringLen(thestring), CP_ACP);
   FileWriteInteger(f, charstotal, INT_VALUE);
   for (int i = 0; i < charstotal; i++) {
     FileWriteInteger(f, sysave[i], CHAR_VALUE);
    }
}

这个函数接收:

  • 文件句柄 f,一个以写入和二进制标志打开的文件
  • 要写入文件的字符串

它会写入字符串的字符长度,并将每个字符存储到文件中。

从文件读取字符串的函数:

string readStringFromFile(int f) {
   string result = "";
   // 加载符号字符串
   char syload[];
   int charstotal = (int)FileReadInteger(f, INT_VALUE);
   if (charstotal > 0) {
     ArrayResize(syload, charstotal, 0);
     for (int i = 0; i < charstotal; i++) {
       syload[i] = (char)FileReadInteger(f, CHAR_VALUE);
    }
     result = CharArrayToString(syload, 0, charstotal, CP_ACP);
  }
   return result;
}

这个函数接收:

  • 文件句柄 f,一个以二进制方式打开的文件

它会读取预期字符的长度,然后逐个读取字符到字符数组中,最后将字符数组转换为字符串返回。

接下来,我们需要一种方法来初始化下载管理器,并从市场观察中填充它:

void grab_symbols() {
   // 仅从市场观察中获取符号
   int s = SymbolsTotal(true);
   ArrayResize(m_symbols, s, 0);
   for (int i = 0; i < ArraySize(m_symbols); i++) {
     m_symbols[i] = SymbolName(i, true);
  }
}

这个过程非常简单:

  • 询问市场观察中有多少个符号(活动的)
  • 调整 m_symbols 数组来接收这些符号
  • 循环获取所有符号的名称

我们还需要一个管理下载符号数据的函数:

void manage(string folder, string filename) {
   // 启动或导航到下一个符号
   if (ArraySize(m_symbols) > 0) {
     // 如果没有开始
     if (!m_started) {
       m_started = true;
       m_current = m_symbols[0];
       m_index = 1;
       save(folder, filename);
       if (_Symbol != m_current) {
         ChartSetSymbolPeriod(ChartID(), m_current, _Period);
      }
      } else {
       m_index++;
       if (m_index <= ArraySize(m_symbols)) {
         m_current = m_symbols[m_index - 1];
       save(folder, filename);
       if (_Symbol != m_current) {
         ChartSetSymbolPeriod(ChartID(), m_current, _Period);
      }
      } else {
       m_finished = true;
       FileDelete(folder + "\" + filename);
       Print("Finished");
       ExpertRemove();
      }
    }
  } else {
     Print("请先获取符号");
  }
}

至于系统是如何工作的:

  • 打开一个图表,需要至少一个图表,并设置一个定时器。
  • 定时器执行后,取消定时器。
  • 检查这是新的下载还是持续的下载。
  • 如果是新的下载,则通过获取所有符号设置它。
  • 如果是持续的下载,则下载当前符号的数据。

在定时器上执行下载的代码部分如下:

void OnTimer() {
   // 如果同步
   if (SymbolIsSynchronized(_Symbol) && TerminalInfoInteger(TERMINAL_CONNECTED) == 1) {
     EventKillTimer();
     // 在这里加载系统
     if (MANAGER.load(MANAGER_FOLDER, MANAGER_STATUS_FILE)) {
       // 系统已加载,正在处理符号
       Comment("系统已加载,正在处理 " + MANAGER.m_current);
       // ticks 加载
       // 首先找到经纪商中可用的最旧 tick
       int attempts = 0;
       int ping = -1;
       datetime cursor = flatten(TimeTradeServer());
       long cursorMSC = ((long)cursor) * 1000;
       long jump = 2592000000; // 60*60*24*30*1000;
       MqlTick receiver[];
       long oldest = LONG_MAX;
       Comment("请稍候");
       while (attempts < 5) {
         ping = CopyTicks(_Symbol, receiver, COPY_TICKS_ALL, cursorMSC, 1);
         if (ping == 1) {
           if (receiver[0].time_msc == oldest) {
             attempts++;
          } else {
           attempts = 0;
        }
         if (receiver[0].time_msc < oldest) {
           oldest = receiver[0].time_msc;
        }
       cursorMSC -= jump;
       if (limitDate && receiver[0].time <= oldestLimit) {
           break;
      }
      } else {
         attempts++;
      }
       Sleep(44);
       Comment("最旧的 Tick : " + TimeToString((datetime)(oldest / 1000), TIME_DATE | TIME_MINUTES | TIME_SECONDS) + " Cursor(" + TimeToString((datetime)(cursorMSC / 1000), TIME_DATE | TIME_MINUTES | TIME_SECONDS) + ") Attempts(" + IntegerToString(attempts) + ") 请等待响应...");
    }
     // 在此时,我们已经获取到最旧的 tick
     // 开始从最旧的请求 ticks 到最新的
     if (oldest != LONG_MAX) {
       ArrayFree(receiver);
       datetime newest_tick = 0;
       // 接收存储在 symbol_time 中的最后一个 tick 时间
       datetime most_recent_candle = (datetime)SymbolInfoInteger(_Symbol, SYMBOL_TIME);
       while (newest_tick < most_recent_candle) {
         // 请求从最旧时间开始的新批次,限制 ticks
         int pulled = CopyTicks(_Symbol, receiver, COPY_TICKS_ALL, oldest, tick_packets);
         if (pulled > 0) {
           // 如果我们拉取了新批次,更新我们的下载时间
           newest_tick = receiver[pulled - 1].time;
           oldest = receiver[pulled - 1].time_msc;
           ArrayFree(receiver);
        }
       // 超时服务器请求,如果想要,可以更改
       Sleep(44);
       Comment("拉取到 " + TimeToString(newest_tick, TIME_DATE | TIME_MINUTES | TIME_SECONDS) + " 到目前为止");
    }
    } else {
       Alert("请关闭终端 前往 ticks 文件夹 并删除空文件夹");
       ExpertRemove();
    }
     // 更新管理器并继续
     MANAGER.manage(MANAGER_FOLDER, MANAGER_STATUS_FILE);
  } else {
     // 抓取市场观察符号以开始下载
     Comment("获取市场观察并开始");
     MANAGER.grab_symbols();
     MANAGER.manage(MANAGER_FOLDER, MANAGER_STATUS_FILE);
  }
}

相关帖子

评论 (0)