美林(Merrill)形态·进阶篇
📐

美林(Merrill)形态·进阶篇

(2/3)· 从 32 种 M/W 子形态到 ATR、RSI 等振荡器映射,手动肉眼筛查在 H1 图表上根本跑不完

含代码示例 第 2/3 篇
很多交易者把美林 M/W 形态当成肉眼图案游戏,在 USDCAD H1 线性图上随便指几个拐点就下单。32 种基础形态叠加六种振荡器数据源,人工回测的覆盖率和偏差都不具备统计意义,外汇贵金属高杠杆下这种随意性容易放大亏损概率。

「EA 初始化时把指标参数一次性灌进去」

MT5 的 OnInit 只跑一次,却决定了后面所有信号计算的口径。这段逻辑里,先调 program.OnInitEvent() 做通用初始化,再用 CreateGUI() 搭交易面板;面板建不起来就直接返回 INIT_FAILED,EA 根本不会往下走。 面板就绪后,连续调用 InitializePrice / InitializeATR / InitializeCCI / InitializeDeM / InitializeForce / InitializeWPR / InitializeRSI,把外部输入的 Inp_* 参数写进类成员。注意 Force 指标最特殊,要同时传周期、均线方法、应用价格和成交量类型四个参,其余指标大多只吃一个周期整数。 这些成员函数本质就是赋值器:比如 InitializeATR(int period) 只做 m_atr_period=period;。你在 MT5 里改 Inp_ATR_Peroid 的输入值,EA 重启后才会通过这里生效,运行时改输入框不会自动热更新。 外汇和贵金属杠杆高、滑点跳空频繁,参数错配可能让信号完全失真。开 MT5 把这段抄进自己的 EA 框架,先打印各 m_*_period 确认初始化值落对了,再接后续计算。

MQL5 / C++
class="type">int OnInit(class="type">void)
  {
class=class="str">"cmt">//---
   program.OnInitEvent();
class=class="str">"cmt">//--- Set the trading panel
   if(!program.CreateGUI())
     {
      ::Print(__FUNCTION__," > Failed to create GUI!");
      class="kw">return(INIT_FAILED);
     }
class=class="str">"cmt">//---
   program.InitializePrice(Inp_Price1);
   program.InitializeATR(Inp_ATR_Peroid);
   program.InitializeCCI(Inp_CCI_Peroid);
   program.InitializeDeM(Inp_DeM_Peroid);
   program.InitializeForce(Inp_ForcePeriod,Inp_ForceMAMethod,Inp_ForceAppliedPrice,Inp_ForceAppliedVolume);
   program.InitializeWPR(Inp_WPR_Period);
   program.InitializeRSI(Inp_RSI_Period);
   class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//---
   class="type">void InitializePrice(ENUM_APPLIED_PRICE price)      { m_applied_price=price;       }
   class="type">void InitializeATR(class="type">int period)                      { m_atr_period=period;         }
   class="type">void InitializeCCI(class="type">int period)                      { m_cci_period=period;         }
   class="type">void InitializeDeM(class="type">int period)                      { m_dem_period=period;         }
   class="type">void InitializeWPR(class="type">int period)                      { m_wpr_period=period;         }
   class="type">void InitializeRSI(class="type">int period)                      { m_rsi_period=period;         }
class="type">void CProgram::InitializeForce(class="type">int period,ENUM_MA_METHOD ma_method,ENUM_APPLIED_PRICE price,ENUM_APPLIED_VOLUME volume)
  {
  m_force_period=period;
  m_force_ma_method=ma_method;
  m_force_applied_price=price;
  m_force_applied_volume=volume;
  }
class="type">bool CProgram::ChangeSymbol1(class="kw">const class="type">long id)
  {
class=class="str">"cmt">//--- Check the element ID
   if(id!=m_symb_table1.Id())
      class="kw">return(false);
class=class="str">"cmt">//--- Exit if the class="type">class="kw">string is not highlighted

从选符号到拉结果:回测前那段不能跳的校验

在 MT5 面板里点完「分析」后,程序先卡一道闸:若左侧符号表返回 WRONG_VALUE,状态栏直接写「Symbol for analysis not selected」并 return(false),后续全不跑。这一步很多人忽略,导致空跑还以为数据坏了。 过了符号校验,代码会把选中的品种名取出来,按 m_lang_index 拼俄语或英语前缀,再把 SYMBOL_DESCRIPTION 塞进状态栏。接着调用 GetResult(symbol),真正的形态扫描才起步。 GetResult 里先 ArrayResize(pattern_types,33) 建了 33 个槽,循环把 0~15 映射 PATTERN_TYPE(i),17~32 映射 PATTERN_TYPE(i-1),中间 16 号故意填 -1 当「无形态」占位。 随后 GetTimeframes 拿到当前勾选的周期数组,若 ArraySize<1 就弹 MessageBox 报错并退出——至少选一个周期,否则后面统计毫无意义。 日期区间由俩日历控件加俩时间编辑框拼出:start/end 用 StringToTime(TimeToString(日历,TIME_DATE)+" 时:分:00") 精确到位。外汇和贵金属波动受时段影响大,这种精确到分钟的区间能避免把低流动性时段混进统计,但杠杆品种高风险仍在,结论仅作概率参考。

MQL5 / C++
if(m_symb_table1.SelectedItem()==WRONG_VALUE)
  {
  class=class="str">"cmt">//--- Show full description of a symbol in the status bar
  m_status_bar.SetValue(class="num">0,"Symbol for analysis not selected");
  m_status_bar.GetItemPointer(class="num">0).Update(true);
  class="kw">return(false);
  }
class=class="str">"cmt">//--- Get a selected symbol
  class="type">class="kw">string symbol=m_symb_table1.GetValue(class="num">0,m_symb_table1.SelectedItem());
class=class="str">"cmt">//--- Show the full symbol description in the status bar
  class="type">class="kw">string val=(m_lang_index==class="num">0)?"Выбранный символ: ":"Selected symbol: ";
  m_status_bar.SetValue(class="num">0,val+::SymbolInfoString(symbol,SYMBOL_DESCRIPTION));
  m_status_bar.GetItemPointer(class="num">0).Update(true);
class=class="str">"cmt">//---
  GetResult(symbol);
  class="kw">return(true);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Handle pattern search results                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CProgram::GetResult(class="kw">const class="type">class="kw">string symbol)
  {
class=class="str">"cmt">//--- Structure for evaluating pattern efficiency
  RATING_SET m_coef[];
class=class="str">"cmt">//--- Figure types
  PATTERN_TYPE pattern_types[];
class=class="str">"cmt">//---
  ArrayResize(pattern_types,class="num">33);
  for(class="type">int i=class="num">0;i<class="num">33;i++)
    {
    if(i==class="num">16)
      pattern_types[i]=-class="num">1;
    if(i<class="num">16)
      pattern_types[i]=PATTERN_TYPE(i);
    if(i>class="num">16)
      pattern_types[i]=PATTERN_TYPE(i-class="num">1);
    }
class=class="str">"cmt">//--- Define selected timeframes
  GetTimeframes(m_timeframes,m_cur_timeframes);
  class="type">int total=ArraySize(m_cur_timeframes);
class=class="str">"cmt">//--- Check for at least one selected timeframe
  if(total<class="num">1)
    {
    if(m_lang_index==class="num">0)
      MessageBox("Вы не выбрали рабочий таймфрейм!","Ошибка",MB_OK);
    else if(m_lang_index==class="num">1)
      MessageBox("You have not selected working timeframe!","Error",MB_OK);
    class="kw">return(false);
    }
  class="type">int count=class="num">0;
  m_total_row=class="num">0;
class=class="str">"cmt">//--- Remove all strings
  m_table1.DeleteAllRows();
class=class="str">"cmt">//--- Get date range
  class="type">class="kw">datetime start=StringToTime(TimeToString(m_calendar1.SelectedDate(),TIME_DATE)+" "+(class="type">class="kw">string)m_time_edit1.GetHours()+":"+(class="type">class="kw">string)m_time_edit1.GetMinutes()+":class="num">00");
  class="type">class="kw">datetime end=StringToTime(TimeToString(m_calendar2.SelectedDate(),TIME_DATE)+" "+(class="type">class="kw">string)m_time_edit2.GetHours()+":"+(class="type">class="kw">string)m_time_edit2.GetMinutes()+":class="num">00");
class=class="str">"cmt">//--- Check selected dates

◍ 形态扫描的日期校验与循环结构

在 MT5 面板里跑形态统计前,先卡死日期区间。若起始时间大于结束时间,或结束时间晚于当前服务器时间 TimeCurrent(),说明用户选的区间无效,程序按语言索引弹俄文或英文报错框并返回 false,不继续烧 CPU。 实际扫描走一个 0~32 的 33 次循环,但下标 16 被 continue 跳过——这意味着系统预留了第 17 个槽位(索引 16)不作为可交易形态,可能是占位或特殊标记。其余 32 个形态若被 m_patterns[k].IsPressed() 判定为选中,才进入该形态的统计分支。 每个选中形态会按 total 个时间帧分别拉数据。GetData 取回的数组若 copied 小于 9,直接弹 Insufficient data 报错;随后只用倒数第 9 根之前的柱做 5 点形态匹配(A~E 连续 5 值),命中 pattern_types[k] 才累加该时间帧的 m_m_total 并调用 GetCategory 算系数。 别把 33 次循环当成 33 种形态。 索引 16 的跳过不是 bug,是设计上的保留位,改循环上限前先确认你没把占位槽当信号源。

MQL5 / C++
if(start>end || end>TimeCurrent())
    {
      if(m_lang_index==class="num">0)
         MessageBox("Неправильно выбран диапазон дат!","Ошибка",MB_OK);
      else if(m_lang_index==class="num">1)
         MessageBox("Incorrect date range selected!","Error",MB_OK);
      class="kw">return(false);
    }
class=class="str">"cmt">//--- 
  for(class="type">int k=class="num">0;k<class="num">33;k++)
    {
      if(k==class="num">16)
         class="kw">continue;
      class=class="str">"cmt">//--- Get selected patterns for analysis
      if(m_patterns[k].IsPressed())
       {
         ArrayResize(m_m_total,total);
         ArrayResize(m_coef,total);
         ZeroMemory(m_m_total);
         ZeroMemory(m_coef);
         count++;
         class=class="str">"cmt">//--- Calculate by timeframes
         for(class="type">int j=class="num">0;j<total;j++)
           {
            class="type">class="kw">double arr[];
            class=class="str">"cmt">//--- Get data for analysis
            class="type">int copied=GetData(m_buttons_group1.SelectedButtonIndex(),symbol,m_cur_timeframes[j],start,end,arr);
            class=class="str">"cmt">//---
            if(copied<class="num">9)
               MessageBox("Insufficient data for analysis","Error",MB_OK);
            for(class="type">int i=class="num">0;i<copied;i++)
             {
               if(i>copied-class="num">9)
                  class="kw">continue;
               class=class="str">"cmt">//--- Pattern search condition
               class="type">class="kw">double A=arr[i];
               class="type">class="kw">double B=arr[i+class="num">1];
               class="type">class="kw">double C=arr[i+class="num">2];
               class="type">class="kw">double D=arr[i+class="num">3];
               class="type">class="kw">double E=arr[i+class="num">4];
               if(GetPatternType(A,B,C,D,E)==pattern_types[k])
                 {
                  m_m_total[j]++;
                  GetCategory(symbol,i+class="num">5,m_coef[j],m_cur_timeframes[j],m_threshold_value1);
                 }
             }
            class=class="str">"cmt">//--- Add the result to the table
            AddRow(m_table1,m_patterns[k].LabelText(),m_coef[j],m_m_total[j],m_cur_timeframes[j]);
           }

「清理表格与多周期图案枚举的落地写法」

当计数变量 count 大于 0 时,说明用户已选定至少一种形态,此时直接删掉表格末行并刷新视图;若 count 为 0,则按语言索引弹窗提示未选图案。这个分支结构能保证面板不会在空选状态下崩溃,外汇与贵金属品种下误操作概率偏高,建议实盘前先用模拟账户跑一遍。 RATING_SET 结构体用 a/b/c 三级分别记录上升与下降趋势的评分,共 6 个 int 字段,后续可据此做多空权重排序。PATTERN_TYPE 枚举把 M1~M16、W1~W16 共 32 种图形硬编码,覆盖分钟与周线级别的镜像形态,调参时若只做黄金 H1 交易,可裁掉 W 系列减小开销。 GetTimeframes 里先建了 22 个字符串的周期数组,从 M1 到 MN,再用按钮按下状态过滤。ArrayResize 调用了两次:第一次给 22 个坑位,第二次按实际选中数 j 收缩。MT5 里若按钮数不等于 22,这段代码会越界,复制时务必核对你的 CButton 数组长度。

MQL5 / C++
   }
    }
class=class="str">"cmt">//---
   if(count>class="num">0)
    {
    class=class="str">"cmt">//---
     m_table1.DeleteRow(m_total_row);
    class=class="str">"cmt">//--- Update the table
     m_table1.Update(true);
     m_table1.GetScrollVPointer().Update(true);
    }
   else
    {
     if(m_lang_index==class="num">0)
       MessageBox("Вы не выбрали паттерн!","Ошибка",MB_OK);
     else if(m_lang_index==class="num">1)
       MessageBox("You have not chosen a pattern!","Error",MB_OK);
    }
   class="kw">return(true);
   }
class="kw">struct RATING_SET
  {
   class="type">int                a_uptrend;
   class="type">int                b_uptrend;
   class="type">int                c_uptrend;
   class="type">int                a_dntrend;
   class="type">int                b_dntrend;
   class="type">int                c_dntrend;
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Figure type                                                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum PATTERN_TYPE
  {
   M1,M2,M3,M4,M5,M6,M7,M8,
   M9,M10,M11,M12,M13,M14,M15,M16,
   W1,W2,W3,W4,W5,W6,W7,W8,
   W9,W10,W11,W12,W13,W14,W15,W16
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Get the array of selected timeframes                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void   CProgram::GetTimeframes(CButton &buttons[],ENUM_TIMEFRAMES &timeframe[])
  {
   class="type">class="kw">string tf[class="num">22]=
    {
      "M1","M2","M3","M4","M5","M6","M10","M12","M15","M20","M30",
      "H1","H2","H3","H4","H6","H8","H12","D1","W1","MN"
    };
   class="type">int j=class="num">0;
   ArrayResize(timeframe,class="num">22);
   for(class="type">int i=class="num">0;i<class="num">22;i++)
    {
     if(buttons[i].IsPressed())
      {
       timeframe[j]=StringToTimeframe(tf[i]);
       j++;
      }
    }
   ArrayResize(timeframe,j);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

把多品种数据塞进同一根数组

做跨品种形态扫描时,最烦的就是每个指标单独写获取逻辑。下面这段 CProgram::GetData 用 index 参数统一调度:0 抓 OHLC 收盘价序列,1~6 分别调 ATR、CCI、DeMarker、Force、WPR、RSI,全部写进同一个 double 引用数组 arr[],调用层不用关心底层句柄。 index==0 走 CopyRates 拿 MqlRates,再按 m_applied_price 在 open/close/high/low 间切换;其余指标走 iATR、iCCI 等标准函数拿 Handle,失败直接 Print 并返回 -1。实盘跑 EURUSD 的 H1,m_atr_period 设 14 时,CopyBuffer 返回的 copied 通常等于 Bars 总数减 1,自己开 MT5 用 Comment(copied) 能立刻验证。 外汇与贵金属杠杆高,指标句柄失效可能发生在品种休市或重连瞬间,别把返回 -1 当普通分支吞掉。 [CODE]int CProgram::GetData(int index,string symb,ENUM_TIMEFRAMES tf,datetime start,datetime end,double &arr[]) { //--- int Handle=INVALID_HANDLE,copied; //--- Close price if(index==0) { MqlRates rt[]; ZeroMemory(rt); copied=CopyRates(symb,tf,start,end,rt); ArrayResize(arr,copied); for(int i=0;i<copied;i++) { arr[i]=rt[i].close; if(m_applied_price==PRICE_OPEN) arr[i]=rt[i].open; else if(m_applied_price==PRICE_CLOSE) arr[i]=rt[i].close; else if(m_applied_price==PRICE_HIGH) arr[i]=rt[i].high; else if(m_applied_price==PRICE_LOW) arr[i]=rt[i].low; } return(copied); } //--- ATR if(index==1) Handle=iATR(symb,tf,m_atr_period,m_applied_price); //--- CCI if(index==2) Handle=iCCI(symb,tf,m_cci_period,m_applied_price); //--- DeMarker if(index==3) Handle=iDeMarker(symb,tf,m_dem_period); //--- Force Index if(index==4) Handle=iForce(symb,tf,m_force_period,m_force_ma_method,m_force_applied_volume); //--- WPR if(index==5) Handle=iWPR(symb,tf,m_wpr_period); //--- RSI if(index==6) Handle=iRSI(symb,tf,m_rsi_period,m_applied_price); //--- if(Handle==INVALID_HANDLE) { Print("Failed to get indicator handle"); return(-1); } copied=CopyBuffer(Handle,0,start,end,arr); return(copied); }[/CODE] 形态判定另有一层 GetPatternType,用 A~E 五个价格点做大小比较。比如 M1 的条件是 B>A 且 A>D 且 D>C 且 C>E,属于左侧双峰右侧单谷的轮廓。你可以把五根 K 线的 close 传进去,看返回是不是 M1,再决定要不要让小布替你跑这套扫描。

MQL5 / C++
class="type">int CProgram::GetData(class="type">int index,class="type">class="kw">string symb,ENUM_TIMEFRAMES tf,class="type">class="kw">datetime start,class="type">class="kw">datetime end,class="type">class="kw">double &arr[])
  {
class=class="str">"cmt">//---
   class="type">int Handle=INVALID_HANDLE,copied;
class=class="str">"cmt">//--- Close price
   if(index==class="num">0)
     {
      class="type">MqlRates rt[];
      ZeroMemory(rt);
      copied=CopyRates(symb,tf,start,end,rt);
      ArrayResize(arr,copied);
      for(class="type">int i=class="num">0;i<copied;i++)
        {
         arr[i]=rt[i].close;
         if(m_applied_price==PRICE_OPEN)
            arr[i]=rt[i].open;
         else if(m_applied_price==PRICE_CLOSE)
            arr[i]=rt[i].close;
         else if(m_applied_price==PRICE_HIGH)
            arr[i]=rt[i].high;
         else if(m_applied_price==PRICE_LOW)
            arr[i]=rt[i].low;
        }
      class="kw">return(copied);
     }
class=class="str">"cmt">//--- ATR
   if(index==class="num">1)
      Handle=iATR(symb,tf,m_atr_period,m_applied_price);
class=class="str">"cmt">//--- CCI
   if(index==class="num">2)
      Handle=iCCI(symb,tf,m_cci_period,m_applied_price);
class=class="str">"cmt">//--- DeMarker
   if(index==class="num">3)
      Handle=iDeMarker(symb,tf,m_dem_period);
class=class="str">"cmt">//--- Force Index
   if(index==class="num">4)
      Handle=iForce(symb,tf,m_force_period,m_force_ma_method,m_force_applied_volume);
class=class="str">"cmt">//--- WPR
   if(index==class="num">5)
      Handle=iWPR(symb,tf,m_wpr_period);
class=class="str">"cmt">//--- RSI
   if(index==class="num">6)
      Handle=iRSI(symb,tf,m_rsi_period,m_applied_price);
class=class="str">"cmt">//---
   if(Handle==INVALID_HANDLE)
     {
      Print("Failed to get indicator handle");
      class="kw">return(-class="num">1);
     }
   copied=CopyBuffer(Handle,class="num">0,start,end,arr);
   class="kw">return(copied);
   }

◍ M 与 W 形态的全排列判定分支

把五根关键价位 A/B/C/D/E 的大小关系穷举完,就能覆盖 M 形与 W 形所有可能的拓扑排列。上面这段逻辑从 M2 一路写到 W13,每个 return 对应一种唯一的高低顺序组合,例如 M2 要求 B>A>D>E>C,即第二峰最高、第一峰次之、随后颈线 D 高于 E 且 E 高于 C。 实际在 MT5 里跑,这类分支最好用枚举或查表替代长串 if,否则 30+ 条判断会让编译器展开后指令数明显膨胀。你可以直接把下面代码贴进自定义指标的信号函数,用 Print() 打出触发的形态代号,验证自己品种上哪种排列出现频率最高。 外汇与贵金属市场跳空频繁,A~E 若取自含跳空 K 线,排列可能失真,触发信号后价格延续概率仅倾向中等,务必结合实时盘口确认。

MQL5 / C++
  if(B>A && A>D && D>E && E>C)
      class="kw">return(M2);
class=class="str">"cmt">//--- M3
  if(B>D && D>A && A>C && C>E)
      class="kw">return(M3);
class=class="str">"cmt">//--- M4
  if(B>D && D>A && A>E && E>C)
      class="kw">return(M4);
class=class="str">"cmt">//--- M5
  if(D>B && B>A && A>C && C>E)
      class="kw">return(M5);
class=class="str">"cmt">//--- M6
  if(D>B && B>A && A>E && E>C)
      class="kw">return(M6);
class=class="str">"cmt">//--- M7
  if(B>D && D>C && C>A && A>E)
      class="kw">return(M7);
class=class="str">"cmt">//--- M8
  if(B>D && D>E && E>A && A>C)
      class="kw">return(M8);
class=class="str">"cmt">//--- M9
  if(D>B && B>C && C>A && A>E)
      class="kw">return(M9);
class=class="str">"cmt">//--- M10
  if(D>B && B>E && E>A && A>C)
      class="kw">return(M10);
class=class="str">"cmt">//--- M11
  if(D>E && E>B && B>A && A>C)
      class="kw">return(M11);
class=class="str">"cmt">//--- M12
  if(B>D && D>C && C>E && E>A)
      class="kw">return(M12);
class=class="str">"cmt">//--- M13
  if(B>D && D>E && E>C && C>A)
      class="kw">return(M13);
class=class="str">"cmt">//--- M14
  if(D>B && B>C && C>E && E>A)
      class="kw">return(M14);
class=class="str">"cmt">//--- M15
  if(D>B && B>E && E>C && C>A)
      class="kw">return(M15);
class=class="str">"cmt">//--- M16
  if(D>E && E>B && B>C && C>A)
      class="kw">return(M16);
class=class="str">"cmt">//--- W1
  if(A>C && C>B && B>E && E>D)
      class="kw">return(W1);
class=class="str">"cmt">//--- W2
  if(A>C && C>E && E>B && B>D)
      class="kw">return(W2);
class=class="str">"cmt">//--- W3
  if(A>E && E>C && C>B && B>D)
      class="kw">return(W3);
class=class="str">"cmt">//--- W4
  if(A>C && C>E && E>D && D>B)
      class="kw">return(W4);
class=class="str">"cmt">//--- W5
  if(A>E && E>C && C>D && D>B)
      class="kw">return(W5);
class=class="str">"cmt">//--- W6
  if(C>A && A>B && B>E && E>D)
      class="kw">return(W6);
class=class="str">"cmt">//--- W7
  if(C>A && A>E && E>B && B>D)
      class="kw">return(W7);
class=class="str">"cmt">//--- W8
  if(E>A && A>C && C>B && B>D)
      class="kw">return(W8);
class=class="str">"cmt">//--- W9
  if(C>A && A>E && E>D && D>B)
      class="kw">return(W9);
class=class="str">"cmt">//--- W10
  if(E>A && A>C && C>D && D>B)
      class="kw">return(W10);
class=class="str">"cmt">//--- W11
  if(C>E && E>A && A>B && B>D)
      class="kw">return(W11);
class=class="str">"cmt">//--- W12
  if(E>C && C>A && A>B && B>D)
      class="kw">return(W12);
class=class="str">"cmt">//--- W13
把形态扫描交给小布盯盘
这些多数据源的美林形态诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到实时 M/W 匹配与效能标注,你只需判断当下趋势环境是否支持形态倾向。

常见问题

每类各 16 种,合计 32 种。美林本人强调其中 6 个子类别(上行、下行、三角形、扩展、头肩、逆头肩)更值得优先研究,其余可做为过滤参考。
线性图剥离了影线噪声,更易识别五点排列;但蜡烛图保留开盘高低,样本不同会导致形态触发率和后续走势概率出现偏移,建议分别回测。
可以。小布盯盘内置了基于收盘价及 ATR、RSI 等振荡器的美林形态扫描,能按品种页直接列出当前匹配的形态类别与历史效能,省去自己写指标的工作量。
振荡器能剔除趋势惯性干扰,在超买超卖区出现的 W/M 反转形态,其后续回归概率可能高于裸价图表,但仍需分样本验证,不存在必然优势。
利维仅做形态意义检验未结合交易规则与样本分层,美林后续引入趋势分类和数据类型拆分才让形态具备可测试结构,绩效才倾向可量化。