时间序列挖掘的数据标签(第1部分):通过EA操作图制作具有趋势标记的数据集·综合运用
(3/3)·手工切分K线标注趋势既慢又易错,这套EA操作图流程让标注变成可视化拖拽
◍ 用键盘在图上打标记并落盘
这段初始化与图表事件逻辑,解决的是「手动在 MT5 图表上按快捷键标注入场/出场点,并自动存成 CSV」的需求。EA 启动时若找不到历史文件,会用 FileOpen 以写入+CSV+读取模式新建一个形如 EURUSD5-1700000000.csv 的文件,并用 iBarShift 反推起点 bar 偏移,把图表滚到末端、画一条叫 Start 的竖线。
初始化尾部连开两个图表事件开关:ChartSetInteger(ChartID(), CHART_EVENT_OBJECT_CREATE, true) 与 CHART_EVENT_OBJECT_DELETE 同理置真,意思是对象被创建或删除时 OnChartEvent 都能吃到回调;最后 ChartRedraw(0) 强制重绘一次,返回 INIT_SUCCEEDED。
真正的交互在 OnChartEvent 的 CHARTEVENT_KEYDOWN 分支。按 B 键时,若 first 为真,记蓝色箭头 OBJ_ARROW_BUY、typ=0、tp="start"、Num 自增 1;否则记红色箭头、typ=1、tp="end"。每次打完 first 取反,对象名拼成 0-1-start 这种格式,方便后续回放。外汇与贵金属波动剧烈,这类手动标注仅作复盘素材,不代表任何方向判定。
ChartNavigate(class="num">0,CHART_END,shift); } else { file_handle = FileOpen(StringFormat("%s%d-%d.csv",Symbol(),Period(),start_t),FILE_WRITE|FILE_CSV|FILE_READ); Print(FileTell(file_handle)); Print("No history file,create file:",StringFormat("%s%d-%d",Symbol(),Period(),start_t)); shift = -iBarShift(Symbol(),PERIOD_CURRENT,(class="type">class="kw">datetime)start_t); ChartNavigate(class="num">0,CHART_END,shift); ObjectCreate(class="num">0,"Start",OBJ_VLINE,class="num">0,(class="type">class="kw">datetime)start_t,class="num">0); } class=class="str">"cmt">//--- Print("EA:",MQL5InfoString(MQL5_PROGRAM_NAME),"Working!"); class=class="str">"cmt">//--- ChartSetInteger(ChartID(),CHART_EVENT_OBJECT_CREATE,true); class=class="str">"cmt">//--- ChartSetInteger(ChartID(),CHART_EVENT_OBJECT_DELETE,true); class=class="str">"cmt">//--- ChartRedraw(class="num">0); class=class="str">"cmt">//--- class="kw">return(INIT_SUCCEEDED); } class="type">void OnChartEvent(class="kw">const class="type">int id, class="kw">const class="type">long &lparam, class="kw">const class="type">class="kw">double &dparam, class="kw">const class="type">class="kw">string &sparam) { class=class="str">"cmt">//Comment(__FUNCTION__,": id=",id," lparam=",lparam," dparam=",dparam," sparam=",sparam); if(id==CHARTEVENT_KEYDOWN) { class="kw">switch(lparam) { case KEY_B: if(first) { col=clrBlue ; typ =class="num">0; Num+=class="num">1; tp = "start"; } else { col=clrRed ; typ = class="num">1; tp = "end"; } ob =OBJ_ARROW_BUY; first = !first; Name = StringFormat("%d-%d-%s",typ,Num,tp); class="kw">break; case KEY_S: if(first)
「图表点击落点与区间标记的落地逻辑」
这段脚本承接键盘事件之后,把鼠标在 MT5 图表上的点击转换成可记录的对象。核心在 CHARTEVENT_CLICK 分支:先用 ChartXYToTimePrice 把像素坐标 x、y 反算成时间 dt 与价格 price,window 固定为 0 表示主图窗口。 若反算成功,ObjectCreate 以之前键盘事件里定好的 Name 和 ob(箭头类型)在主图画出标记,并用 ObjectSetInteger 写入颜色 col。start 类标记会把 dt 存进 Start 变量;end 类则在文件句柄存在时调用 file_write(Start,dt) 写出区间起止时间。 键盘分支里 typ 非 3 时才响应点击,typ 等于 3 即视为已完成一轮标记,不再画新对象。删除或创建对象时仅 Print 出名称做日志,不影响标记流程。 开 MT5 把这段接进 EA 的 OnChartEvent,连按两次键切换红蓝箭头后点图表,就能看到 1/0 型编号对象生成;外汇与贵金属波动剧烈,标记仅作主观区间参考,实际触发概率随品种流动性变化。
if(id==CHARTEVENT_CLICK&&(typ!=class="num">3)) { class=class="str">"cmt">//--- definition class="type">int x=(class="type">int)lparam; class="type">int y=(class="type">int)dparam; class="type">class="kw">datetime dt =class="num">0; class="type">class="kw">double price =class="num">0; class="type">int window=class="num">0; if(ChartXYToTimePrice(class="num">0,x,y,window,dt,price)) { ObjectCreate(class="num">0,Name,ob,window,dt,price); ObjectSetInteger(class="num">0,Name,OBJPROP_COLOR,col); class=class="str">"cmt">//Print("time:",dt,"shift:",iBarShift(Symbol(),PERIOD_CURRENT,dt)); if(tp=="start") Start=dt; else { if(file_handle) file_write(Start,dt); } ChartRedraw(class="num">0); } else Print("ChartXYToTimePrice class="kw">return error code: ",GetLastError()); } class=class="str">"cmt">//--- object class="kw">delete if(id==CHARTEVENT_OBJECT_DELETE) { Print("The object with name ",sparam," has been deleted"); } class=class="str">"cmt">//--- object create if(id==CHARTEVENT_OBJECT_CREATE) { Print("The object with name ",sparam," has been created!"); } } if (id==CHARTEVENT_CLICK&&(typ!=class="num">3)) typ=class="num">3; class="type">void file_write(class="type">class="kw">datetime start,
画得少,看得清
上面这段把 K 线抽取和落盘收尾连在一起:CopyRates 拉完当前周期数据后,若文件指针恰在 2 字节位置(即刚写过标记),先补一行表头 time/open/high/low/close/tick_volume/trend_group/trend_index,再逐根写柱;否则直接续写,不重复表头。 读侧用 read_csv 从文件句柄一直读到 FileIsEnding,每读一行 ArrayResize 扩一位再存字符串,回测或离线分析时直接吃这个数组就行。 OnDeinit 里 FileClose 并打「Write data!」,保证 EA 卸载时缓冲区落盘不丢。外汇与贵金属波动剧烈、杠杆风险高,这套导出只解决数据搬运,进出场仍靠你自己的价格行为判断。
class="type">class="kw">datetime end) { class="type">MqlRates rates[]; ArraySetAsSeries(rates,false); class="type">int n_cp=CopyRates(Symbol(),PERIOD_CURRENT,start,end,rates); if(n_cp>class="num">0) { if(FileTell(file_handle)==class="num">2) { FileWrite(file_handle,"time","open","high","low","close","tick_volume","trend_group","trend_index"); for(class="type">int i=class="num">0; i<n_cp; i++) { FileWrite(file_handle, rates[i].time, rates[i].open, rates[i].high, rates[i].low, rates[i].close, rates[i].tick_volume, typ, i); } } else { for(class="type">int i=class="num">0; i<n_cp; i++) { FileWrite(file_handle, rates[i].time, rates[i].open, rates[i].high, rates[i].low, rates[i].close, rates[i].tick_volume, typ, i); } } } else Print("No data copied!"); FileFlush(file_handle); typ=class="num">3; } class="type">void read_csv(class="type">int hd, class="type">class="kw">string &arry[]) { class="type">int i=class="num">0; class="kw">while(!FileIsEnding(hd)) { ArrayResize(arry,i+class="num">1); arry[i]=FileReadString(hd); i++; } } class="type">void OnDeinit(class="kw">const class="type">int reason) { FileClose(file_handle); Print("Write data!"); }