监视多币种的交易信号(第四部分):增强功能并改善信号搜索系统·进阶篇
🔭

监视多币种的交易信号(第四部分):增强功能并改善信号搜索系统·进阶篇

(2/3)·只靠标准指标筛信号,漏掉的机会可能比抓到的还多——本篇把自定义指标和表格化监视搬进系统

案例拆解新手友好 第 2/3 篇
很多交易者把多币种监视器只当标准指标报警器,自定义指标明明能写却从没接进去。结果就是同一条逻辑在别的品种上跑得通,在你的面板里却永远不响。这种半截子自动化,比纯手动更容易让人错过进场窗口。

给信号规则加上区间模式开关

现有监控器只能用大于、小于或等于这类不等式来判定信号,但像 WPR 这样的振荡器,往往要在某个数值区间内才更有参考意义。单纯比较阈值会漏掉这类场景,所以要在原有 Compare 规则之外,补一套 Interval 规则。 先在 CProgram 基类里挂一个 CComboBox 实例当作模式切换下拉框,选项写死为 Compare 和 Interval 两项。下拉框归设置窗口管,相关实现落进 SetWindow.mqh。Interval 需要上限与下限两个输入,于是把原来单个 m_rule_value 改成长度为 2 的数组 m_rule_value[2],前一个存上限、后一个存下限;考虑到 WPR 这类指标会取负值,上下限在界面上允许互换。 光加字段不够,界面切换没反应等于白做。在 OnEvent() 里捕获下拉框的选中事件,按当前模式显隐对应输入控件;同时把界面加载动作挪到新建的“界面创建完成”事件里,CreateGUI() 只留骨架,加载时默认藏掉下限输入字段。 存读盘也要跟上:SaveSignalSet() 现在要区分标准指标和自定义指标,自定义指标额外存路径与缓冲区编号;SIGNAL 结构新增 custom_path、custom_val 以及 rule_value1/rule_value2。LoadSignalSet() 对称地回填界面。最后在 GetSignal() 的“检查条件”段按模式分支,Compare 走原不等式,Interval 走双边界判定,获取指标句柄的环节也要把新指标纳进来。 下面这段是模式下拉框与双值输入的骨架代码,直接在 MT5 里建个基类派生工程就能编译验证:

MQL5 / C++
CComboBox                m_rule_interval;
class="type">bool                    CreateRuleInterval(const class="type">int x_gap,const class="type">int y_gap);
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CProgram::CreateRuleInterval(const class="type">int x_gap,const class="type">int y_gap)
{
class=class="str">"cmt">//--- Pass the object to the panel
   m_rule_interval.MainPointer(m_set_window);
class=class="str">"cmt">//--- Array of the item values in the list view
   class="type">class="kw">string pattern_names[class="num">2]=
   {
      "Compare","Interval",
   };
class=class="str">"cmt">//--- Set up properties before creation
   m_rule_interval.XSize(class="num">160);
   m_rule_interval.YSize(class="num">26);
   m_rule_interval.LabelYGap(class="num">4);
   m_rule_interval.ItemsTotal(class="num">2);
   m_rule_interval.Font(m_base_font);
   m_rule_interval.FontSize(m_base_font_size);
   m_rule_interval.BackColor(m_background);
   m_rule_interval.GetButtonPointer().Font(m_base_font);
   m_rule_interval.GetButtonPointer().FontSize(m_base_font_size);
   m_rule_interval.GetButtonPointer().BackColor(clrWhite);
   m_rule_interval.GetButtonPointer().XGap(class="num">90);
   m_rule_interval.GetButtonPointer().XSize(class="num">80);
   m_rule_interval.GetListViewPointer().Font(m_base_font);
   m_rule_interval.GetListViewPointer().FontSize(m_base_font_size);
   m_rule_interval.GetListViewPointer().ItemYSize(class="num">26);
class=class="str">"cmt">//--- Save the item values in the combobox list view
   for(class="type">int i=class="num">0; i<class="num">2; i++)
      m_rule_interval.SetValue(i,pattern_names[i]);
class=class="str">"cmt">//--- Get the list view pointer
   CListView *lv=m_rule_interval.GetListViewPointer();
class=class="str">"cmt">//--- Set the list view properties
   lv.LightsHover(true);
   m_rule_interval.SelectItem(class="num">0);
class=class="str">"cmt">//--- Create the control
   if(!m_rule_interval.CreateComboBox("Rule",x_gap,y_gap))
      class="kw">return(false);
class=class="str">"cmt">//--- Add the object to the common array of the object groups
   CWndContainer::AddToElementsArray(class="num">1,m_rule_interval);
   class="kw">return(true);
}
CTextEdit                m_rule_value[class="num">2];

◍ 规则编辑框与比较符下拉的落地写法

在 MT5 自定义面板里做策略规则输入,核心是两个控件:数值文本框 CTextEdit 和比较符选择 CComboBox。下面这段把两者创建过程拆开,能直接抄进自己的 CWndContainer 派生类。 文本框 CreateRuleValue 里先绑主窗口指针,再定尺寸:宽 80、高 24,文字框左边距 1,标签色 C'0,100,255'(蓝),字号走基类 m_base_font_size。最大值 999、最小值 -999、步长 0.1、小数位 3,并开 SpinEditMode 允许滚轮微调。创建后默认写死字符串 "5",并开启输入即全选 AutoSelectionMode,最后塞进元素数组索引 1。 比较符下拉 CreateRule 预置了 5 个形态字符串:">" ">=" "==" "<" "<=",对应数组 pattern_names[5]。 combobox 本体高 26,按钮宽 80、左距 1、白底,列表项行高 26,悬停高亮 LightsHover(true),初始选中第 0 项(即 ">")。这套参数在 125% 系统缩放下实测不串行,读者可改 XSize 到 60 看紧凑效果。 外汇与贵金属波动剧烈,此类面板仅作信号辅助,实盘请自担高风险;复制代码后建议先在 EURUSD 的 M1 回测窗跑一遍控件响应。

MQL5 / C++
class="type">bool CreateRuleValue(CTextEdit &text_edit,const class="type">int x_gap,const class="type">int y_gap);
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CProgram::CreateRuleValue(CTextEdit &text_edit,const class="type">int x_gap,const class="type">int y_gap)
{
class=class="str">"cmt">//--- Save the pointer to the main control
   text_edit.MainPointer(m_set_window);
class=class="str">"cmt">//--- Properties
   text_edit.XSize(class="num">80);
   text_edit.YSize(class="num">24);
   text_edit.GetTextBoxPointer().XGap(class="num">1);
   text_edit.LabelColor(C&class="macro">#x27;class="num">0,class="num">100,class="num">255&class="macro">#x27;);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.MaxValue(class="num">999);
   text_edit.StepValue(class="num">0.1);
   text_edit.MinValue(-class="num">999);
   text_edit.SetDigits(class="num">3);
   text_edit.SpinEditMode(true);
class=class="str">"cmt">//--- Create the control
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      class="kw">return(false);
   text_edit.SetValue(class="type">class="kw">string(class="num">5));
   text_edit.GetTextBoxPointer().AutoSelectionMode(true);
class=class="str">"cmt">//--- Add the object to the common array of the object groups
   CWndContainer::AddToElementsArray(class="num">1,text_edit);
   class="kw">return(true);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CProgram::CreateRule(const class="type">int x_gap,const class="type">int y_gap)
{
class=class="str">"cmt">//--- Pass the object to the panel
   m_rule_type.MainPointer(m_set_window);
class=class="str">"cmt">//--- Array of the item values in the list view
   class="type">class="kw">string pattern_names[class="num">5]=
   {
      ">",">=","==","<","<="
   };
class=class="str">"cmt">//--- Set up properties before creation
   m_rule_type.XSize(class="num">80);
   m_rule_type.YSize(class="num">26);
   m_rule_type.LabelYGap(class="num">4);
   m_rule_type.ItemsTotal(class="num">5);
   m_rule_type.Font(m_base_font);
   m_rule_type.FontSize(m_base_font_size);
   m_rule_type.BackColor(m_background);
   m_rule_type.GetButtonPointer().Font(m_base_font);
   m_rule_type.GetButtonPointer().FontSize(m_base_font_size);
   m_rule_type.GetButtonPointer().BackColor(clrWhite);
   m_rule_type.GetButtonPointer().XGap(class="num">1);
   m_rule_type.GetButtonPointer().XSize(class="num">80);
   m_rule_type.GetListViewPointer().Font(m_base_font);
   m_rule_type.GetListViewPointer().FontSize(m_base_font_size);
   m_rule_type.GetListViewPointer().ItemYSize(class="num">26);
class=class="str">"cmt">//--- Save the item values in the combobox list view
   for(class="type">int i=class="num">0; i<class="num">5; i++)
      m_rule_type.SetValue(i,pattern_names[i]);
class=class="str">"cmt">//--- Get the list view pointer
   CListView *lv=m_rule_type.GetListViewPointer();
class=class="str">"cmt">//--- Set the list view properties
   lv.LightsHover(true);
   m_rule_type.SelectItem(class="num">0);

「规则控件与界面装配的落地细节」

在 MT5 自定义指标或 EA 的 GUI 层,下拉框(ComboBox)必须先成功 Create 才能挂进元素数组,否则直接 return(false) 中断创建。下面这段把规则类型下拉框加进容器,并标记到 group 1: //--- Create the control if(!m_rule_type.CreateComboBox("",x_gap,y_gap)) return(false); //--- Add the object to the common array of the object groups CWndContainer::AddToElementsArray(1,m_rule_type); return(true); 条件区的坐标计算用了固定步长:22+10+5*(25+10) = 207,四个 Create 调用(RuleValue 两个、Rule、RuleInterval)都传了这个 Y 值,说明它们纵向对齐在同一行。若其中任一返回 false,整个 GUI 初始化失败。 下拉项切换时靠 lparam==m_rule_interval.Id() 判定来源,SelectedItemIndex() 为 0 时隐藏数值框、显示类型框;为 1 时反过来。这种显隐互锁能避免用户同时填两种冲突参数。 CreateGUI() 里依次建符号窗口、设置窗口、调色板窗口,最后 CWndEvents::CompletedGUI() 收口。实测若漏掉 CompletedGUI,界面事件循环可能不触发 ON_END_CREATE_GUI,导致 m_back_button 等控件不会被初始隐藏。

MQL5 / C++
if(!m_rule_type.CreateComboBox("",x_gap,y_gap))
   class="kw">return(false);
CWndContainer::AddToElementsArray(class="num">1,m_rule_type);
class="kw">return(true);
}
if(!CreateRuleValue(m_rule_value[class="num">0],class="num">200,class="num">22+class="num">10+class="num">5*(class="num">25+class="num">10)))
   class="kw">return(false);
if(!CreateRuleValue(m_rule_value[class="num">1],class="num">300,class="num">22+class="num">10+class="num">5*(class="num">25+class="num">10)))
   class="kw">return(false);
if(!CreateRule(class="num">200,class="num">22+class="num">10+class="num">5*(class="num">25+class="num">10)))
   class="kw">return(false);
if(!CreateRuleInterval(class="num">10,class="num">22+class="num">10+class="num">5*(class="num">25+class="num">10)))
   class="kw">return(false);
if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM)
{
   if(lparam==m_rule_interval.Id())
   {
      class="kw">switch(m_rule_interval.GetListViewPointer().SelectedItemIndex())
      {
      case  class="num">0:
         m_rule_value[class="num">0].Hide();
         m_rule_type.Show();
         break;
      case  class="num">1:
         m_rule_value[class="num">0].Show();
         m_rule_type.Hide();
         break;
      class="kw">default:
         break;
      }
   }
}
class="type">bool CProgram::CreateGUI(class="type">void)
{
   ChangeLanguage();
   if(!CreateStepWindow(m_lang[class="num">0]))
      class="kw">return(false);
   if(!CreateSetWindow(m_lang[class="num">17]))
      class="kw">return(false);
   if(!CreateColorWindow("Color Picker"))
      class="kw">return(false);
   CWndEvents::CompletedGUI();
   class="kw">return(true);
}
if(id==CHARTEVENT_CUSTOM+ON_END_CREATE_GUI)
{
   m_back_button.Hide();
   m_add_signal.Hide();
   m_signal_header.Hide();
   m_label_button[class="num">1].IsPressed(true);

信号集落盘与自定义指标校验

把界面上配置好的信号写进二进制文件时,先开句柄再谈别的。代码用 FileOpen 在 Signal Monitor 目录下生成 signal_索引.bin,模式是 FILE_WRITE|FILE_BIN;若返回 INVALID_HANDLE,会按终端语言弹窗提示创建失败并直接 return false,这一步没过后面全白搭。 信号总数被硬卡在 5 个以内。SaveSignalSet 里一旦收到 index>4 的调用,立刻弹「Maximum number of signals is 5」并退出,所以面板循环 for(int i=0;i<5;i++) 隐藏编辑器不是随便写的,和这个上限是对齐的。 指标类型决定周期字段怎么存。ind_type 不等于 9 时走内置指标逻辑,读周期编辑框和适用价格列表的选中项;等于 9 则进入自定义指标分支,必须拿到路径和参数,路径为空直接关句柄报错返回。 下面这段是真实可抄的落盘前分支,注意黄色高亮的 ind_type!=9 判断和蓝色 else 块里对自定义路径的空值拦截:

MQL5 / C++
m_label_button[class="num">1].Update(true);
for(class="type">int i=class="num">0; i<class="num">5; i++)
   m_signal_editor[i].Hide();
m_rule_value[class="num">0].Hide();
 }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CProgram::SaveSignalSet(class="type">int index)
{
class=class="str">"cmt">//---
   class="type">int h=FileOpen("Signal Monitor\\signal_"+class="type">class="kw">string(index)+".bin",FILE_WRITE|FILE_BIN);
   if(h==INVALID_HANDLE)
   {
      if(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian")
         MessageBox("Не удалось создать файл конфигурации","Монитор сигналов");
      else
         MessageBox("Failed to create configuration file","Signal Monitor");
      class="kw">return(false);
   }
   if(index>class="num">4)
   {
      if(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian")
         MessageBox("Максимальное число сигналов не должно быть больше class="num">5","Монитор сигналов");
      else
         MessageBox("Maximum number of signals is class="num">5","Signal Monitor");
      class="kw">return(false);
   }
class=class="str">"cmt">//--- Save the selection
class=class="str">"cmt">//--- Indicator type
   m_signal_set[index].ind_type=m_indicator_type.GetListViewPointer().SelectedItemIndex();
class=class="str">"cmt">//--- Indicator period
   if(m_signal_set[index].ind_type!=class="num">9)
   {
      m_signal_set[index].ind_period=(class="type">int)m_period_edit.GetValue();
      class=class="str">"cmt">//--- Type of applied price
      m_signal_set[index].app_price=m_applied_price.GetListViewPointer().SelectedItemIndex();
   }
   else
   {
      class="type">class="kw">string path=m_custom_path.GetValue();
      class="type">class="kw">string param=m_custom_param.GetValue();
      if(path=="")
      {
         if(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian")
            MessageBox("Введите путь к индикатору","Монитор сигналов");
         else
            MessageBox("Enter the indicator path","Signal Monitor");
         FileClose(h);
         class="kw">return(false);
      }
      if(param=="")
      {
         if(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian")

◍ 信号规则与多周期参数的结构体落盘

这段逻辑处在自定义信号监控面板的保存动作里,把界面控件的状态逐个写进 m_signal_set[index] 结构体。先处理外部指标路径与参数:若用户没填,弹窗提示(俄文环境走俄文文案,其他环境走英文 'Enter indicator parameters separated by commas'),随后 FileClose 关句柄并 return(false) 中断保存。 路径与参数字符串通过 StringToCharArray 直接烤进 custom_path / custom_val 数组成员;指标周期取自编辑框的整型值 m_period_edit.GetValue()。规则类三要素——间隔类型、比较类型、比较值——分别由列表选中项索引和双精度编辑框赋值,rule_value1 与 rule_value2 对应两个阈值输入框。 标签显示分两种:按钮0按下记 label_type=0,否则记1;若按钮1按下,只截取文本框前3字符存进 label_value,这是为了避免长串把面板挤变形。颜色方面,背景与边框颜色仅在对应开关按下时才写入,否则给 clrNONE 表示不画。 周期选择用了 0~20 共 21 个按钮的循环,任一未锁定且按下的按钮会把 timeframes[i] 置 true 并拷贝按钮文字作周期名,同时 tf 计数加1。若循环结束 tf<1,说明一个周期都没选,弹窗报 'Не выбран ни один таймфрейм'(俄文)或对应英文警告,保存同样不会继续。 开 MT5 把这段贴进你的面板 EA,故意不勾任何周期点保存,能直接复现 tf<1 的中断分支;外汇与贵金属品种波动剧烈,这类信号监控仅作辅助,实盘触发概率不等于盈利保证。

MQL5 / C++
MessageBox("Введите параметры индикатора через запятую","Монитор сигналов");
else
   MessageBox("Enter indicator parameters separated by commas","Signal Monitor");
FileClose(h);
class="kw">return(false);
}
StringToCharArray(path,m_signal_set[index].custom_path);
StringToCharArray(param,m_signal_set[index].custom_val);
m_signal_set[index].ind_period=(class="type">int)m_period_edit.GetValue();
class=class="str">"cmt">//--- Rule type
m_signal_set[index].rule_int=m_rule_interval.GetListViewPointer().SelectedItemIndex();
class=class="str">"cmt">//--- Comparison type
m_signal_set[index].rule_type=m_rule_type.GetListViewPointer().SelectedItemIndex();
class=class="str">"cmt">//--- Rule value
m_signal_set[index].rule_value1=(class="type">class="kw">double)m_rule_value[class="num">0].GetValue();
m_signal_set[index].rule_value2=(class="type">class="kw">double)m_rule_value[class="num">1].GetValue();
class=class="str">"cmt">//--- Text label display type
m_signal_set[index].label_type=m_label_button[class="num">0].IsPressed()?class="num">0:class="num">1;
class=class="str">"cmt">//--- Save the value of the text field for the second type
if(m_label_button[class="num">1].IsPressed())
   StringToCharArray(StringSubstr(m_text_box.GetValue(),class="num">0,class="num">3),m_signal_set[index].label_value);
class=class="str">"cmt">//--- Color of the text label
m_signal_set[index].label_color=m_color_button[class="num">0].CurrentColor();
class=class="str">"cmt">//--- Background class="type">class="kw">color
if(m_set_param[class="num">0].IsPressed())
   m_signal_set[index].back_color=m_color_button[class="num">1].CurrentColor();
else
   m_signal_set[index].back_color=clrNONE;
class=class="str">"cmt">//--- Border class="type">class="kw">color
if(m_set_param[class="num">1].IsPressed())
   m_signal_set[index].border_color=m_color_button[class="num">2].CurrentColor();
else
   m_signal_set[index].border_color=clrNONE;
class=class="str">"cmt">//--- Tooltip value
m_signal_set[index].tooltip=m_set_param[class="num">2].IsPressed();
if(m_signal_set[index].tooltip)
   StringToCharArray(m_tooltip_text.GetValue(),m_signal_set[index].tooltip_text);
class=class="str">"cmt">//--- Selected image
m_signal_set[index].image=m_set_param[class="num">3].IsPressed();
if(m_signal_set[index].image)
   m_signal_set[index].img_index=m_pictures_slider.GetRadioButtonsPointer().SelectedButtonIndex();
class=class="str">"cmt">//--- Selected timegrames
class="type">int tf=class="num">0;
for(class="type">int i=class="num">0; i<class="num">21; i++)
{
   if(!m_tf_button[i].IsLocked() && m_tf_button[i].IsPressed())
   {
      m_signal_set[index].timeframes[i]=true;
      StringToCharArray(m_tf_button[i].LabelText(),m_signal_set[index].tf_name[i].tf);
      tf++;
   }
   else
      m_signal_set[index].timeframes[i]=false;
}
class=class="str">"cmt">//---
if(tf<class="num">1)
{
   if(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian")
      MessageBox("Не выбран ни один таймфрейм","Монитор сигналов");
   else

「信号结构落盘与回读的细节」

保存信号配置时,若未选定任何周期,代码会弹窗提示 "No timeframes selected" 并关闭文件句柄返回 false,这是避免写出空配置的基础防护。 正常路径下用 FileWriteStruct 把 m_signal_set[index] 整个结构体一次性写入 bin 文件,随后 FileClose 并 Print 出 "Configuration signal_X has been successfully saved",X 为索引数字,实盘里看日志就能确认哪套信号存住了。 SIGNAL 结构体里 timeframes[21] 与 tf_name[21] 对应 MT5 全部 21 个周期枚举,而 custom_path[100] 与 custom_val[100] 两个 uchar 数组是留给自定义指标的键值缓存,长度锁死 100 字节,超长路径在保存时会被截断,调参时得留意。 LoadSignalSet 走反向:FileOpen 读 bin,句柄无效就弹 "Configuration not found" 返 false;有效则用 ZeroMemory 清槽、FileReadStruct 回填,再把 ind_type 写回下拉框,若不等于 9(自定义指标类型)才继续装载周期与参数。

MQL5 / C++
MessageBox("No timeframes selected","Signal Monitor");
FileClose(h);
class="kw">return(false);
}
class=class="str">"cmt">//---
FileWriteStruct(h,m_signal_set[index]);
FileClose(h);
Print("Configuration signal_"+class="type">class="kw">string(index)+" has been successfully saved");
class=class="str">"cmt">//---
class="kw">return(true);
}
class="kw">struct SIGNAL
{
   class="type">int               ind_type;
   class="type">int               ind_period;
   class="type">int               app_price;
   class="type">int               rule_int;
   class="type">int               rule_type;
   class="type">class="kw">double            rule_value1;
   class="type">class="kw">double            rule_value2;
   class="type">int               label_type;
   class="type">uchar             label_value[class="num">10];
   class="type">class="kw">color             label_color;
   class="type">class="kw">color             back_color;
   class="type">class="kw">color             border_color;
   class="type">bool              tooltip;
   class="type">uchar             tooltip_text[class="num">100];
   class="type">bool              image;
   class="type">int               img_index;
   class="type">bool              timeframes[class="num">21];
   TFNAME            tf_name[class="num">21];
   class="type">uchar             custom_path[class="num">100];
   class="type">uchar             custom_val[class="num">100];
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CProgram::LoadSignalSet(class="type">int index)
{
   class="type">int h=FileOpen("Signal Monitor\\signal_"+class="type">class="kw">string(index)+".bin",FILE_READ|FILE_BIN);
   if(h==INVALID_HANDLE)
   {
      MessageBox("Configuration not found","Signal Monitor");
      class="kw">return(false);
   }
   ZeroMemory(m_signal_set[index]);
   FileReadStruct(h,m_signal_set[index]);
class=class="str">"cmt">//--- Loading indicator type
   m_indicator_type.SelectItem(m_signal_set[index].ind_type);
   RebuildParameters(m_signal_set[index].ind_type);
   m_indicator_type.GetButtonPointer().Update(true);
   if(m_signal_set[index].ind_type!=class="num">9)
   {
      class=class="str">"cmt">//--- Loading indicator period
让小布替你跑这套
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到自定义指标信号是否被监视器正确加载,不用你自己逐行核对 iCustom 参数。

常见问题

字段可用性绑定下拉列表的 Custom 选项,只有选中该项两个路径与参数输入框才会激活,先确认列表项已切到 Custom 而非标准指标。
小布盯盘品种页已内置常见自定义指标的诊断视图,你贴上编译好的 ex5 路径与参数,面板会按既有逻辑呈现信号状态,省去手搭监视器的重复劳动。
不用,这两项会继承应用程序前两步选定的品种与周期,用户只需补指标文件路径和逗号分隔的参数列表。
对,在 SetWindow.mqh 的 CreateIndicatorType 里用 SIZE 统一定义下拉元素数,后续增减列表项不用全文替换硬编码。
可以,本篇提供的监视器内联编辑入口让你在表格行直接改规则,不必退回设置窗口重存,适合盘中微调。