MT5报价导出其他程序实战
用MQL5脚本将行情归档为外部分析格式
一、为什么需要将MT5报价导出到其他应用程序
许多交易者在深入MQL5编程之前,会使用NeuroShell DayTrader、Excel、Tradematic等第三方工具做策略原型或数据分析。这类程序往往要求特定历史数据格式,而MT5默认保存的CSV日期和时间格式并不兼容。过去在MT4时代就有不少复制报价的脚本,MT5同样可以开发类似工具,避免手动在Excel里反复编辑表头和日期,节省大量重复劳动。
需要注意的是,MT5本身已能覆盖绝大多数量化需求,导出数据不是为了替代平台,而是作为多工具协同的桥梁。例如某些股票或期货程序不支持直接接入经纪商,就可以用MT5统一下载外汇与部分品种数据再转发。
二、目标数据格式与脚本设计范围
文章以NeuroShell DayTrader Professional(NSDT)为例。NSDT 5与6版本对格式要求不同:5版日期时间分两列,表头为引号包裹的字段名;6版日期时间同列,表头为逗号分隔无引号。MT5原生CSV无法直接套用,因为日期呈现为yyyy.mm.dd hh:mm,而目标可能是dd.mm.yyyy或dd/mm/yyyy。
脚本支持三种归档范围:仅当前图表品种、市场报价窗口所有品种、服务器全部可用品种。用户可通过下拉参数选择表头格式、日期分隔符及写入品种模式,并指定起止日期。这样一套脚本就能适配多种外部程序,而不必为每个软件写单独版本。
- NSDT_5: "Date" "Time" "Open" ... 分列
- NSDT_6: Date,Open,High... 同列日期时间
- 支持点号或斜杠作为日期分隔符
- 支持当前/市场报价/全部品种三种模式
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)+")";
// ... 其余标签赋值与创建
}
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);
}
七、文件夹创建与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或自定义列序。
八、常见坑与多方案对比
- 手动Excel改格式易错且无法批量,脚本一次配置反复用
- OBJ_EDIT面板在测试器可见,标签对象不可见,选错类型调试困难
- 日期分隔符与表头必须匹配,否则第三方程序拒读
- CopyRates受终端最大柱数限制,超量需调设置或分段
- 用IsStopped保障用户中断时清理对象,遗漏会留垃圾对象
相比直接用MT5内置导出,本脚本价值在灵活性与自动化:可跨几十品种循环、自定义路径、实时进度。若仅偶尔取一个品种,终端右击保存足矣;但系统化工作流仍推荐此类脚本。