首页 / 交易学院 / 小布知识库 / MT5报价导出其他程序实战
MT5报价导出其他程序实战
📤

MT5报价导出其他程序实战

用MQL5脚本将行情归档为外部分析格式

MQL5开发 难度 · 进阶 2026-05-17 7 分钟阅读
#MQL5#数据导出#CSV#行情处理

一、为什么需要将MT5报价导出到其他应用程序

许多交易者在深入MQL5编程之前,会使用NeuroShell DayTrader、Excel、Tradematic等第三方工具做策略原型或数据分析。这类程序往往要求特定历史数据格式,而MT5默认保存的CSV日期和时间格式并不兼容。过去在MT4时代就有不少复制报价的脚本,MT5同样可以开发类似工具,避免手动在Excel里反复编辑表头和日期,节省大量重复劳动。

需要注意的是,MT5本身已能覆盖绝大多数量化需求,导出数据不是为了替代平台,而是作为多工具协同的桥梁。例如某些股票或期货程序不支持直接接入经纪商,就可以用MT5统一下载外汇与部分品种数据再转发。

✦ 适用场景
当你需要在非MT5环境做可视化、神经网络训练或跨市场回测时,本文脚本可直接生成兼容文件。

二、目标数据格式与脚本设计范围

文章以NeuroShell DayTrader Professional(NSDT)为例。NSDT 5与6版本对格式要求不同:5版日期时间分两列,表头为引号包裹的字段名;6版日期时间同列,表头为逗号分隔无引号。MT5原生CSV无法直接套用,因为日期呈现为yyyy.mm.dd hh:mm,而目标可能是dd.mm.yyyy或dd/mm/yyyy。

脚本支持三种归档范围:仅当前图表品种、市场报价窗口所有品种、服务器全部可用品种。用户可通过下拉参数选择表头格式、日期分隔符及写入品种模式,并指定起止日期。这样一套脚本就能适配多种外部程序,而不必为每个软件写单独版本。

enum FORMAT_HEADERS
  {
   NSDT_5 = 0, // "Date" "Time" "Open" "High" "Low" "Close" "Volume"
   NSDT_6 = 1  // Date,Open,High,Low,Close,Volume
  };
enum FORMAT_DATETIME
  {
   SEP_POINT1 = 0, // dd.mm.yyyy hh:mm
   SEP_POINT2 = 1, // dd.mm.yyyy, hh:mm
   SEP_SLASH1 = 2, // dd/mm/yyyy hh:mm
   SEP_SLASH2 = 3  // dd/mm/yyyy, hh:mm
  };
enum CURRENT_MARKETWATCH
  {
   CURRENT          = 0, // ONLY CURRENT SYMBOLS
   MARKETWATCH      = 1, // MARKETWATCH SYMBOLS
   ALL_LIST_SYMBOLS = 2  // ALL LIST SYMBOLS
  };

三、脚本外部参数与用户输入校验

外部参数使用input声明,包括起止日期、表头格式、日期格式、品种模式、是否清空市场报价、是否显示进度。启动后若参数矛盾,例如起始日期晚于结束日期,或表头与日期格式不匹配(NSDT_5只能配逗号分隔日期时间),脚本会弹窗报错并终止,防止生成废文件。

input datetime            start_date     = D'01.01.2011';
input datetime            end_date       = D'18.09.2012';
input FORMAT_HEADERS      format_headers = NSDT_5;
input FORMAT_DATETIME     format_date    = SEP_POINT2;
input CURRENT_MARKETWATCH curr_mwatch    = CURRENT;
input bool                clear_mwatch   = true;
input bool                show_progress  = true;

ValidationParameters函数集中处理这些逻辑。它返回true表示有错,OnStart直接return。这种前置检查在批量导出时尤其重要,因为多品种循环中可能某个品种参数不合法,提前拦截可避免中途部分写入导致混乱。

bool ValidationParameters()
  {
   if(start_date>=end_date)
     {
      MessageBox("The start date should be earlier than the ending one!","Parameter error!",MB_ICONERROR);
      return(true);
     }
   if(format_headers==NSDT_5 && (format_date==SEP_POINT1 || format_date==SEP_SLASH1))
     {
      MessageBox("Header and date/time formats do not match!","Error",MB_ICONERROR);
      return(true);
     }
   if(format_headers==NSDT_6 && (format_date==SEP_POINT2 || format_date==SEP_SLASH2))
     {
      MessageBox("Header and date/time formats do not match!","Error",MB_ICONERROR);
      return(true);
     }
   return(false);
  }

四、全局变量与信息面板构建

脚本使用MqlRates数组复制K线,字符串数组存品种名,另有一系列变量记录路径、进度、格式化片段。信息面板用OBJ_EDIT对象作背景,优势在于EA测试可视化模式下也能显示,而矩形标签和位图在测试中不可见。面板展示品种序号、路径、周期、起止日期、服务器首根数据、复制柱数及进度等,帮助用户实时监控。

MqlRates rates[];
string symbols[];
string arr_nmobj[22]={"fon","hd01","nm01",...};
string path="";
int cnt_symb=0, sz_arr_symb=0, bars=0, copied_bars=0;
double pgs_pcnt=0;
int hFl=INVALID_HANDLE;
string sdt="",dd="",mm="",yyyy="",tm="",sep="";

InfoTable函数负责绘制。它调用SeriesInfoInteger获取指定周期与M1的首数据日期,并用Edit和Label对象布局。若show_progress为假则面板高度缩小,以提升写入速度。所有对象名存入数组,便于退出时统一删除。

void InfoTable(int s)
  {
   // ... 创建编辑框背景与标签
   Create_Edit(0,0,arr_nmobj[0],"",corner,fnt,fnt_sz,clrDimGray,clrDimGray,345,height_pnl,xV3,yV1,2,C'15,15,15');
   arr_txtobj[2]=""+symbols[s]+" ("+IS(s+1)+"/"+IS(cnt_symb)+")";
   // ... 其余标签赋值与创建
  }
⚠ 对象清理
脚本结束或被删除时必须调用DelAllScriptObjects,否则图表残留面板对象影响手动操作。
void DelAllScriptObjects()
  {
   int sz_arr1=ArraySize(arr_nmobj);
   for(int i=0; i<sz_arr1; i++) { DelObjbyName(arr_nmobj[i]); }
  }

五、主体逻辑:品种遍历与数据复制

OnStart是入口。先校验参数,获取终端最大柱数,调用GetSymbolsToArray填充品种数组。SetSeparateForFormatDate依格式设分隔符。随后循环每个品种:刷新面板、GetDataCurrentSymbol取数、CheckCreateGetPath建目录、WriteDataToFile写文件。

void OnStart()
  {
   if(ValidationParameters()) { return; }
   max_bars=TerminalInfoInteger(TERMINAL_MAXBARS);
   GetSymbolsToArray();
   sz_arr_symb=ArraySize(symbols);
   SetSeparateForFormatDate();
   for(int s=0; s<=sz_arr_symb-1; s++)
     {
      copied_bars=0; pgs_pcnt=0.0;
      InfoTable(s); ChartRedraw();
      int res=GetDataCurrentSymbol(s);
      if(res==0) { BC }
      if(res==2) { DelAllScriptObjects(); Print("User deleted"); break; }
      if((path=CheckCreateGetPath(s))=="") { BC }
      WriteDataToFile(s);
     }
   DelSymbolsFromMarketWatch();
   Sleep(1000); DelAllScriptObjects();
  }

GetSymbolsToArray根据curr_mwatch决定数组内容。若仅当前品种,直接赋_Symbol;否则用SymbolsTotal和SymbolName遍历。MWatchOrAllList指针函数返回true表示取市场报价,false取全部列表,避免重复代码块。

void GetSymbolsToArray()
  {
   if(curr_mwatch==CURRENT) { ArrayResize(symbols,1); symbols[0]=_Symbol; }
   if(curr_mwatch==MARKETWATCH || curr_mwatch==ALL_LIST_SYMBOLS)
     {
      cnt_symb=SymbolsTotal(MWatchOrAllList());
      for(int i=0; i<=cnt_symb-1; i++)
        {
         ArrayResize(symbols,i+1);
         symbols[i]=SymbolName(i,MWatchOrAllList());
        }
     }
  }

BC宏根据模式决定break或continue:单品种出错整体停,多品种跳过继续。IsStopped检测用户删除脚本,留约3秒清理。这种细粒度控制让批量任务更健壮。

#define BC if(curr_mwatch==CURRENT) { break; } if(curr_mwatch==MARKETWATCH || curr_mwatch==ALL_LIST_SYMBOLS) { continue; }

六、历史数据检查与复制细节

GetDataCurrentSymbol中先调CheckLoadHistory(原文引自参考,负责下载或确认深度),依返回码记录日志。成功则用CopyRates按起止时间复制至rates数组,copied_bars记录数量。若复制失败返回0触发BC。注意CopyRates时间参数用datetime,check_start_date来自校验后值。

int GetDataCurrentSymbol(int s)
  {
   int res=CheckLoadHistory(s,_Period);
   switch(res)
     {
      case -1: Print("Unknown symbol"); return(0);
      case -4: Print("Download failed"); return(0);
      case -3: Print("Interrupted"); return(2);
      // ...
     }
   if(CopyRates(symbols[s],_Period,check_start_date,end_date,rates)<=0)
     { Print("Copy error"); return(0); }
   copied_bars=ArraySize(rates);
   return(1);
  }
✦ 数据深度
终端设置的最大柱数可能限制复制量,CheckLoadHistory会回退到可用数据并提示,不必强行改设置。

七、文件夹创建与CSV写入实现

CheckCreateGetPath在MQL5/Files下建DATA_OHLC根目录及品种子目录,返回全路径。写文件时文件名如EURUSD_H1.csv,FileOpen以CSV ANSI逗号分隔打开,已有则覆盖。先写表头,再循环rates:用StringSubstr拆时间,StringConcatenate拼成目标格式,FileWrite逐行输出OHLCV。

void WriteDataToFile(int s)
  {
   int dgt=(int)SymbolInfoInteger(symbols[s],SYMBOL_DIGITS);
   string nm_fl=path+symbols[s]+"_"+gStrTF(_Period)+".csv";
   hFl=FileOpen(nm_fl,FILE_WRITE|FILE_CSV|FILE_ANSI,',');
   if(hFl>0)
     {
      if(format_headers==NSDT_5) FileWrite(hFl,"\"Date\" "\"Time\" ...");
      if(format_headers==NSDT_6) FileWrite(hFl,"Date","Open",...);
      for(int i=0; i<=copied_bars-1; i++)
        {
         sdt=TSdm(rates[i].time);
         yyyy=StringSubstr(sdt,0,4); mm=StringSubstr(sdt,5,2); dd=StringSubstr(sdt,8,2); tm=StringSubstr(sdt,11);
         StringConcatenate(sdt,dd,sep,mm,sep,yyyy,sep_dt_tm,tm);
         FileWrite(hFl,sdt,DS_dgt(rates[i].open,dgt),...);
        }
      FileClose(hFl);
     }
  }

整个流程结束可选清空市场报价中不活动图表品种。该脚本展示了从参数、UI、IO到异常处理的完整MQL5脚本范式,稍作修改即可支持其他格式如Finam的tick或自定义列序。

八、常见坑与多方案对比

⚠ 反向导入
原文讨论区提到从Finam等导入MT5只能做指标,不能改经纪商数据;如需统一测试建议开支持品种的经纪商账户。

相比直接用MT5内置导出,本脚本价值在灵活性与自动化:可跨几十品种循环、自定义路径、实时进度。若仅偶尔取一个品种,终端右击保存足矣;但系统化工作流仍推荐此类脚本。

常见问题

编写脚本声明enum定义表头与日期格式,用input让用户输入起止日期和模式;OnStart中遍历SymbolName获取品种,CopyRates复制MqlRates,StringSubstr拆时间后StringConcatenate拼行,FileOpen(FILE_CSV)写文件,参考本文WriteDataToFile函数。
在脚本中用StringSubstr把time转为yyyy、mm、dd、tm片段,按FORMAT_DATETIME枚举用点号或斜杠连接,NSDT_5用逗号分隔日期时间两列,NSDT_6同列,表头用FileWrite写对应引号或纯文本。
设外部参数curr_mwatch=MARKETWATCH,GetSymbolsToArray调用SymbolsTotal(true)和SymbolName(i,true)填充数组,循环每个品种建DATA_OHLC下子目录并写CSV,用BC宏处理单品种失败跳过。
使用OBJ_EDIT对象作背景和文本,因矩形标签与位图在策略测试可视化模式不显示;InfoTable函数创建编辑框并ChartRedraw刷新,show_progress控制是否绘进度百分比。
TerminalInfoInteger(TERMINAL_MAXBARS)读取限制;CheckLoadHistory若返回-2表示超量,脚本退而求其次用可用数据并写日志,用户可改终端选项最大柱数或缩短期段重新运行。