请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  量化策略 帖子:3364712 新帖:0

研究烛条分析技术(第二部分):自动搜索新形态

Peace发表于:4 月 17 日 18:45回复(1)

内容目录

  • 概述
  • 判定任务
  • 更新界面原型
  • 工具实现
  • 展示
  • 结束语

概述

在 前一篇文章 中已分析过现有的烛条分析方法。 我们发现它们不是通用的,且不适合所有条件。 相反,在运用它们之前,您需要针对特定的金融产品、时间帧等进行附加的验证。 即,特定条件下可以运用哪些形态。

专门设计的方法还可以针对选定的金融产品或一组产品逐一分析每种形态。 它还允许在预定义参数(包括时间帧或样本范围)之间找到可能的相关性,以及特别的衡量数据,例如出现频率,在形态形成后特定方向上的走势概率。

分析方法仅涉及从大量现有烛条样式中选择出的 14 种形态。 由于不可能逐一分析所有形态,所以找到了另一种解决方案。 之前验证所有形态的关键特征是依据它们的基础,即它们的组成部分。 已分析烛条形态当中,有四种由一根烛条组成,其余十种由两根烛条组成。 已分析烛条形态由六种烛条类型的不同集合或序列所组成。

形态的主要问题是它们很久之前出现,而市场属性、行为和动态则是持续变化。 为了与市场分析趋势保持一致,应在形态分析中进行一些变通。 在本文中,我们研究一套基于已知烛条类型搜索并测试新烛条形态的系统。 


判定任务

为了开发新的烛条形态生成算法,我们需要定义关键规则:

  • 新形态将由一根、两根或三根简单的烛条类型组成。
  • 简单的烛条类型有:长烛条,短烛条,尖顶,十字星,marubozu 和锤子。
  • 烛条类型将基于方向划分:多头和空头。 十字星烛条除外。
  • 简单的烛条类型可以反复。 示例:两个空头长烛条的形态。

创建新形态的一般规则如图例 1 所示。


图例1 新形态创建算法。

因此,我们有了一个烛条形态池,新的形态将在其中形成。 这些新形态将包含 1-3 根烛条,有或没有反复。 这个形态池将完全包含 11 种基本烛条。 生成的烛条形态将根据相同的原理进行分析,这在 第一篇文章 中已有阐述。


更新界面原型

将会有一个单独的选项卡用于操控生成的形态。 该选项卡将包含与现有 分析 选项卡中所提供的相似元素。 仅有的区别是第一列名为 Set,且表格大小不会被固定。

图例 2 用于操控生成的形态的选项卡。

第三个选项卡 设置 已被大幅修改。 在此选项卡中正确配置参数是必不可少的,这就是为什么我们将会更详尽地研究它。

图例 3 更新的 “设置” 选项卡。

  1. 添加了简单烛条类型,及其名称的编号。 因此,我们可以轻松地将视觉表现与 “所用蜡烛” 列表相对应。
  2. 添加了选择界面语言的选项。 俄语或英语。
  3. '所用蜡烛' 列表,即形态的参与者。 复选框允许选择所需的测试类型。
  4. 一组可切换按钮。 只能选择一个位置。 这样我们就可以避免在 AutoSearch 选项卡中重载生成的形态列表。
  5. 两个可切换按钮。 '反复'(叠加)意味着形态中只能有一个烛条类型。 示例是由三根空头 Marubozu 烛条组成的形态。 与此同时,尖顶 - Marubozu - Marubozu 形态也可以存在。

工具实现

我们已判定在现有应用程序中需要实现的关键附加项。 现在我们来继续实现它。 首先,我们需要添加一个额外的选项卡。 选项卡的索引将会变化。 AutoSearch 选项卡的索引为 1,此编号之前用于 设置 选项卡。 现在 “设置” 使用编号 2。 当链接子元素时必须要考虑这一点。 所以,我们需要将 “设置” 选项卡所有子元素的索引从 1 更改为 2。

//+------------------------------------------------------------------+
//| 创建一个选项卡组                                                    |
//+------------------------------------------------------------------+
bool CProgram::CreateTabs(const int x_gap,const int y_gap)
  {
#define TABS1_TOTAL 3
//--- 将指针存储到主控件
   m_tabs1.MainPointer(m_window1);
//--- 属性
   m_tabs1.IsCenterText(true);
   m_tabs1.PositionMode(TABS_TOP);
   m_tabs1.AutoXResizeMode(true);
   m_tabs1.AutoYResizeMode(true);
   m_tabs1.AutoXResizeRightOffset(3);
   m_tabs1.AutoYResizeBottomOffset(25);

//--- 添加指定属性的选项卡
   string tabs_names[TABS1_TOTAL]={"Analysis","Auto search","Settings"};
   for(int i=0; i<TABS1_TOTAL; i++)
      m_tabs1.AddTab(tabs_names[i],150);
//--- 创建控件
   if(!m_tabs1.CreateTabs(x_gap,y_gap))
      return(false);
//--- 将对象添加到公共对象组数组中
   CWndContainer::AddToElementsArray(0,m_tabs1);
   return(true);
  }

AutoSearch 选项卡的子元素与 分析 选项卡的子元素类似。 仅有的区别是第一列的名称和配色方案。 这允许在视觉上区分这些标签的界面元素。

//+------------------------------------------------------------------+
//| 为控件创建窗体                                                      |
//+------------------------------------------------------------------+
bool CProgram::CreateWindow(const string caption_text)
  {
//--- 将指针添加到窗口数组
   CWndContainer::AddWindow(m_window1);
//--- 属性
   m_window1.XSize(750);
   m_window1.YSize(500);
   m_window1.FontSize(9);
   m_window1.IsMovable(true);
   m_window1.CloseButtonIsUsed(true);
   m_window1.CollapseButtonIsUsed(true);
   m_window1.FullscreenButtonIsUsed(true);
   m_window1.TooltipsButtonIsUsed(true);
//--- 创建窗体
   if(!m_window1.CreateWindow(m_chart_id,m_subwin,caption_text,5,5))
      return(false);
//--- 选卡
   if(!CreateTabs(3,43))
      return(false);
//--- “分析” 选卡
//--- 可编辑字段
   if(!CreateSymbolsFilter(m_symb_filter1,10,10,"Symbols",0))
      return(false);
   if(!CreateRequest(m_request1,250,10,"Search",0))
      return(false);
   if(!CreateRange(m_range1,485,10,"Range",0))
      return(false);
//--- 组合框
   if(!CreateComboBoxTF(m_timeframes1,350,10,"Timeframe",0))
      return(false);
//--- 创建品种表格
   if(!CreateSymbTable(m_symb_table1,10,50,0))
      return(false);
//--- 创建结果表格
   if(!CreateTable1(m_table1,120,50,0))
      return(false);

//--- “AutoSearch” 选卡
//--- 可编辑字段
   if(!CreateSymbolsFilter(m_symb_filter2,10,10,"Symbols",1))
      return(false);
   if(!CreateRequest(m_request2,250,10,"Search",1))
      return(false);
   if(!CreateRange(m_range2,485,10,"Range",1))
      return(false);
//--- 组合框
   if(!CreateComboBoxTF(m_timeframes2,350,10,"Timeframe",1))
      return(false);
//--- 创建品种表格
   if(!CreateSymbTable(m_symb_table2,10,50,1))
      return(false);
//--- 创建结果表格
   if(!CreateTable2(m_table2,120,50,1))
      return(false);

从上面的代码中您可以看出,区别只涉及两个方法 CreateTable1()CreateTable2()。 现在,我们进入 “设置” 选项卡。 这里的第一个变化涉及图形资源。 烛条参数设置元素也已添加到创建元素中。 这是由 CreateNameCandle() 方法完成的。

//+------------------------------------------------------------------+
//| 创建烛条设置元素                                                    |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\settings_dark.bmp"
#resource "\\Images\\EasyAndFastGUI\\Candles\\long.bmp"
#resource "\\Images\\EasyAndFastGUI\\Candles\\short.bmp"
#resource "\\Images\\EasyAndFastGUI\\Candles\\doji.bmp"
#resource "\\Images\\EasyAndFastGUI\\Candles\\spin.bmp"
#resource "\\Images\\EasyAndFastGUI\\Candles\\maribozu.bmp"
#resource "\\Images\\EasyAndFastGUI\\Candles\\hammer.bmp"
//---
bool CProgram::CreateCandle(CPicture &pic,CButton &button,CTextLabel &candlelabel,const string candlename,const int x_gap,const int y_gap,string path)
  {
//--- 将指针存储到主控件
   pic.MainPointer(m_tabs1);
//--- 附加到选卡
   m_tabs1.AddToElementsArray(2,pic);
//--- 属性
   pic.XSize(64);
   pic.YSize(64);
   pic.IconFile(path);
//--- 创建按钮
   if(!pic.CreatePicture(x_gap,y_gap))
      return(false);
//--- 将指向元素的指针添加到基类
   CWndContainer::AddToElementsArray(0,pic);
   CreateButtonPic(pic,button,"Images\\EasyAndFastGUI\\Icons\\bmp16\\settings_dark.bmp");
   CreateNameCandle(candlelabel,x_gap,y_gap+pic.YSize(),candlename);
   return(true);
  }

这就是全部有关已创建控件的变化。 现在我们继续讨论新内容。 其中之一是界面语言选择选项。 该应用程序支持两种语言:俄语和英语。 语言选择如图例 4 所示。

图例 4 界面语言选择。

语言变更通过 ChangeLanguage() 方法执行。 根据其逻辑,文本组件被替换。 方法代码非常简单。 这是它在事件处理程序中的应用程序:

//--- 选择组合框中的项目
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM)
     {
      //--- 时间帧变化
      if(ChangePeriod1(lparam))
         Update(true);
      //--- 更改界面语言
      if(ChangeLanguage(lparam))
         Update(true);
     }

从代码中可以看出,当选择组合框中的项目时,语言变更方法会判断下拉菜单中的哪些项目被选择,并据其设置相应的语言索引:

//+------------------------------------------------------------------+
//| 更改界面语言                                                       |
//+------------------------------------------------------------------+
bool CProgram::ChangeLanguage(const long id)
  {
//--- 检查元素 ID
   if(id!=m_lang_setting.Id())
      return(false);
   m_lang_index=m_lang_setting.GetListViewPointer().SelectedItemIndex();
//---
   if(m_lang_index==0)
     ....

下一部分负责选择简单的烛条类型,而测试形态也会从其中产生。 有 11 种类型。 该控件的实现,作为烛条类型的名称列表,并含复选框,可由它们反映所选项目。 CreateListView() 方法用来实现这个逻辑:

//+------------------------------------------------------------------+
//| 创建列表                                                          |
//+------------------------------------------------------------------+
bool CProgram::CreateListView(const int x_gap,const int y_gap)
  {
//--- 列表视图的大小
#define CANDLE_TOTAL 11
//--- 将指针存储到主控件
   m_listview1.MainPointer(m_tabs1);
//--- 附加到选卡
   m_tabs1.AddToElementsArray(2,m_listview1);
//--- 属性
   m_listview1.XSize(175);
   m_listview1.YSize(250);
   m_listview1.ItemYSize(19);
   m_listview1.LabelXGap(25);
   m_listview1.LightsHover(true);
   m_listview1.CheckBoxMode(true);
   m_listview1.ListSize(CANDLE_TOTAL);
   m_listview1.AutoYResizeMode(true);
   m_listview1.AutoYResizeBottomOffset(10);
   m_listview1.FontSize(10);
//--- 用数据填充列表视图
   string cand_name[CANDLE_TOTAL]=
     {
      "Long — bullish",
      "Long — bearish",
      "Short — bullish",
      "Short — bearish",
      "Spinning Top — bullish",
      "Spinning Top — bearish",
      "Doji",
      "Marubozu — bullish",
      "Marubozu — bearish",
      "Hammer — bullish",
      "Hammer — bearish"
     };
   for(int r=0; r<CANDLE_TOTAL; r++)
     {
      m_listview1.SetValue(r,(string)(r+1)+". "+cand_name[r]);
     }
//--- 创建列表视图
   if(!m_listview1.CreateListView(x_gap,y_gap))
      return(false);
//--- 将指向元素的指针添加到基类
   CWndContainer::AddToElementsArray(0,m_listview1);
   return(true);
  }

接下来的两个控件与简单烛条类型列表直接相关。 第一个是开关,它启用或禁用反复。 正如在任务定义部分中已经提及的,反复仅与由一根烛条类型组成的形态有关,而与其大小无关。 

图例 5 选择所分析的烛条类型和反复模式。

负责创建反复开关的方法称为 CreateDualButton()

//+------------------------------------------------------------------+
//| 创建反复开关                                                       |
//+------------------------------------------------------------------+
bool CProgram::CreateDualButton(CButton &lbutton,CButton &rbutton,const int x_gap,const int y_gap,const string ltext,const string rtext)
  {
   CreateButton(lbutton,x_gap,y_gap,ltext);
   CreateButton(rbutton,x_gap+99,y_gap,rtext);
   return(true);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateButton(CButton &button,const int x_gap,const int y_gap,const string text)
  {
//--- 将指针保存到主控件
   button.MainPointer(m_tabs1);
//--- 附加到选卡
   m_tabs1.AddToElementsArray(2,button);
//--- 属性
   button.XSize(100);
   button.YSize(30);
   button.Font("Trebuchet");
   button.FontSize(10);
   button.IsCenterText(true);
   button.BorderColor(C'0,100,255');
   button.BackColor(clrAliceBlue);
   button.BackColorLocked(C'50,180,75');
   button.BorderColorLocked(C'50,180,75');
   button.LabelColorLocked(clrWhite);
//--- 创建控件
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- 将指向元素的指针添加到基类
   CWndContainer::AddToElementsArray(0,button);
   return(true);
  }

在左键单击事件中跟踪控件操作设置:

   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
     {
      ....
      //--- 如果按下按钮
      if(lparam==m_button7.Id())
        {
         m_button7.IsLocked(true);
         m_button8.IsLocked(false);
        }
      else if(lparam==m_button8.Id())
        {
         m_button7.IsLocked(false);
         m_button8.IsLocked(true);
        }

已分析应用程序的可视部分后,我们现在进入计算部分。 首先,我们需要判定这些动作的最小设置、操作顺序和处理方法:

步骤 1. 设置输入数据。

在开始运行应用程序之前,我们需要选择简单的烛条类型,从中生成分析形态。 然后选择是否启用反复模式。 反复模式意味着在创建任何大小的形态时仅使用一根烛条的类型。 然后,选择形态中的烛条数量。 这也许包括一根、两根或三根烛条的形态。 请注意,若要生成两根或三根烛条的形态,必须至少选择两根简单烛条。 如果您尝试生成烛条较少的形态,应用程序将返回错误,如图例 6 所示。

图例 6 错误:两根烛条形态却仅选择了一根烛条类型。

步骤 2. 处理 “AutoSearch” 选项卡。

设置正确的输入参数后,切换到 “AutoSearch” 并选择一个品种进行测试。 我们来详细查看此选项卡中表现的产品能力。

  • 选择并搜索正在分析的货币品种。 在输入字段中,您可以键入品种的一部分,或指定所需品种并以逗号分隔,然后单击 “搜索” 按钮。 您还可以使用预定义短语 Major,它显示主要货币对。 若要查看市场观察窗口中的所有可用品种,您应取消选中左上角的选择框。 这会禁用搜索窗口中的过滤。
  • 然后从下拉列表中选择所需的时间帧,并在所选时间帧内划定样本范围的烛条数量。
  • 选择所需的产品或列表后,单击当前的产品或列表开始分析。 之后,应用程序将生成在步骤 1 中配置的形态,执行相应的计算,并在表格中显示数据。 在表格中接收数据之后,您可以更改时间帧,并重新计算实时数据。

我们来详细查看结果数据表格。 当我们进一步考察算法时,这有助于理解其计算和操作原理。


图例 7 EURUSD 对的数据计算示例。  

从图例 7 中可以看出,正分析货币对的所在行已在品种表格中被选择。 M15 时间帧和 8000 根 15 分钟烛条的样品范围显示在右侧部分的顶部。 结果表格有六列。 此处我们只考虑第一列,而其余的列在第一篇文章的 开发界面原型 部分已有说明。

第一列是 Set。 在下面的行中,数值按 [1,1,2] 的格式显示。 方括号中的三个数字表示使用三根烛条的形态。 该形态由简单的烛条 1 和 2 组成,完全按方括号中指定的顺序排列。 用过的烛条编号可以在 设置 选卡的 用过的蜡烛 部分找到。

图例 8 形态生成中用过的烛条编号和列表。  

现在我们知道如何配置和启动应用程序,因此我们可以继续考察其内部操作逻辑。 设置输入数据后,我们点击品种表格中的货币产品。 单击表格单元格的响应会调用 ChangeSymbol2() 方法:

//--- 在列表或表格中选择项目的事件处理
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM)
     {
      //--- 品种改变
      if(ChangeSymbol1(lparam))
         Update(true);
      if(ChangeSymbol2(lparam))
         m_table2.Update(true);
     }

在此方法中,除了在状态栏中设置信息值之外,还有更多的两个方法。

//+------------------------------------------------------------------+
//| 品种改变                                                          |
//+------------------------------------------------------------------+
bool CProgram::ChangeSymbol2(const long id)
  {
//--- 检查元素 ID
   if(id!=m_symb_table2.Id())
      return(false);
//--- 如果该行并非高亮显示,则退出
   if(m_symb_table2.SelectedItem()==WRONG_VALUE)
     {
      //--- 在状态栏中显示完整的品种说明
      m_status_bar.SetValue(0,"No symbol selected for analysis");
      m_status_bar.GetItemPointer(0).Update(true);
      return(false);
     }
//--- 获取品种
   string symbol=m_symb_table2.GetValue(0,m_symb_table2.SelectedItem());
//--- 在状态栏中显示完整的品种说明
   string val=(m_lang_index==0)?"Выбранный символ: ":"Selected symbol: ";
   m_status_bar.SetValue(0,val+::SymbolInfoString(symbol,SYMBOL_DESCRIPTION));
   m_status_bar.GetItemPointer(0).Update(true);
   if(!BuildingAutoSearchTable())
      return(false);
   GetPatternType(symbol,m_total_combination);
   return(true);
  }

第一个方法是 BuildingAutoSearchTable()。 它按照 设置 选项卡中简单烛条列表选择的项目创建形态,在 用过的蜡烛(图例 8)中,将其显示在结果表格的第一列。

//+------------------------------------------------------------------+
//| 重建形态自动搜索表格                                                 |
//+------------------------------------------------------------------+
bool CProgram::BuildingAutoSearchTable(void)
  {
//---
   if(!GetCandleCombitaion())
     {
      if(m_lang_index==0)
         MessageBox("Число выбранных свечей меньше размера исследуемого паттерна!","Ошибка");
      else if(m_lang_index==1)
         MessageBox("The number of selected candles is less than the size of the studied pattern!","Error");
      return(false);
     }
//--- 删除所有行
   m_table2.DeleteAllRows();
//--- 按品种数量设置行数
   for(int i=0; i<ArraySize(m_total_combination); i++)
     {
      m_table2.AddRow(i);
      m_table2.SetValue(0,i,m_total_combination[i]);
     }
   m_table2.DeleteRow(ArraySize(m_total_combination));
//--- 更新表格
   m_table2.Update(true);
   m_table2.GetScrollVPointer().Update(true);
   m_table2.GetScrollHPointer().Update(true);
   return(true);
  }
//+------------------------------------------------------------------+
//| 基于简单烛条生成形态                                                 |
//+------------------------------------------------------------------+
bool CProgram::GetCandleCombitaion(void)
  {
   string candlenumber[];
   int selected_candles=0,n;
   ArrayResize(candlenumber,m_total_candles);
//---
   for(int i=0;i<m_total_candles;i++)
     {
      if(m_listview1.GetState(i))
        {
         candlenumber[selected_candles]=(string)(i+1);
         selected_candles++;
        }
     }

   if((m_pattern_size==2 && selected_candles<2) || (m_pattern_size==3 && selected_candles<2) || selected_candles<1)
      return(false);
//--- 计算组合数
   if(m_pattern_size>1)
      n=(m_button7.IsLocked())?(int)MathPow(selected_candles,m_pattern_size):(int)MathPow(selected_candles,m_pattern_size)-selected_candles;
   else
      n=selected_candles;
   ArrayResize(m_total_combination,n);

   n=0;
//--- 单根烛条的一个集合
   if(m_pattern_size==1)
     {
      for(int i=0;i<selected_candles;i++)
         m_total_combination[i]="["+candlenumber[i]+"]";
     }
//--- 两根烛条的一个集合
   else if(m_pattern_size==2)
     {
      //--- 启用反复模式
      if(m_button7.IsLocked())
        {
         for(int i=0;i<selected_candles;i++)
           {
            for(int j=0;j<selected_candles;j++)
              {
               m_total_combination[n]="["+candlenumber[i]+","+candlenumber[j]+"]";
               n++;
              }
           }
        }
      //--- 禁用反复模式
      else if(m_button8.IsLocked())
        {
         for(int i=0;i<selected_candles;i++)
           {
            for(int j=0;j<selected_candles;j++)
              {
               if(j!=i)
                 {
                  m_total_combination[n]="["+candlenumber[i]+","+candlenumber[j]+"]";
                  n++;
                 }
              }
           }
        }
     }
//--- 三根烛条的集合
   else if(m_pattern_size==3)
     {
      //--- 启用反复模式
      if(m_button7.IsLocked())
        {
         for(int i=0;i<selected_candles;i++)
           {
            for(int j=0;j<selected_candles;j++)
              {
               for(int k=0;k<selected_candles;k++)
                 {
                  m_total_combination[n]="["+candlenumber[i]+","+candlenumber[j]+","+candlenumber[k]+"]";
                  n++;
                 }
              }
           }
        }
      //--- 禁用反复模式
      else if(m_button8.IsLocked())
        {
         for(int i=0;i<selected_candles;i++)
           {
            for(int j=0;j<selected_candles;j++)
               for(int k=0;k<selected_candles;k++)
                 {
                  if(i==j && i==k)
                     continue;
                  m_total_combination[n]="["+candlenumber[i]+","+candlenumber[j]+","+candlenumber[k]+"]";
                  n++;
                 }
           }
        }
     }
   return(true);
  }

基于烛条集合创建形态,由 GetCandleCombination() 方法执行。 该方法的目的是基于所选烛条的序列号显示所有可能的简单烛条组合,有或没有反复,且考虑了形态部分中所选的烛条数大小。 

所有组合都写入字符串数组 m_total_combitaion[],并在 BuildingAutoSearchTable() 方法中将数据添加到结果表格之中。

第二个方法在 BuildingAutoSearchTable() 之后调用,负责根据生成的形态结果列表计算和显示其他数据。 我们更详细地考察 GetPatternType() 方法 - 它在第一篇文章中用于计算预定义形态,但它已被简化。 此任务的目的是识别图表上的预设形态,并进行分析。 由于此方法用来搜索已存在的形态,以及所生成的形态,因此实现了方法重载:

   //--- 识别形态
   bool              GetPatternType(const string symbol);
   bool              GetPatternType(const string symbol,string &total_combination[]);

在第二个变体中,我们使用 先前创建的含有所生成形态的字符串数据数组。 该方法的目的是识别图表上生成的任何形态,对其进行评估并计算其效率。 在第一篇文章的 任务定义 部分中提供了该思路和算法描述。 因此,我们现在不再赘述。

评估所发现形态效率早前是用数组,我们为上行趋势和下行趋势添加了类别评估 A、B 和 C。 在本文中,我们将数组转换为结构。 这样可以缩短生成的代码。 该结构如下:

struct RATING_SET
  {
   int               a_uptrend;
   int               b_uptrend;
   int               c_uptrend;
   int               a_dntrend;
   int               b_dntrend;
   int               c_dntrend;
  };

 对于我们的任务,每个生成的形态我们都需要这组估计。 所以,在方法的开头声明 RATING_SET 结构数组。 数组大小对应于所生成形态数量,或字符串数组 m_total_combitaion[] 的大小。

   RATING_SET ratings[];

//---
   total_patterns=ArraySize(total_combination);
...
   ArrayResize(ratings,total_patterns);

此数组存储所有生成的形态,但这些形态在表格结果的第一列中会以字符串显现。 因此,下一阶段是从每个字符串中提取使用过的简单烛条形态的索引,并将它们从 CANDLE_STRUCTURE 转换为烛条类型。

struct CANDLE_STRUCTURE
  {
   double            open,high,low,close;       // OHLC
   TYPE_TREND        trend;                     //趋势
   bool              bull;                      //多头
   double            bodysize;                  //空头
   TYPE_CANDLESTICK  type;                      //烛条类型
  };

为此,我们将进行一些转换。 此外,我们将考虑另一种方法,该方法有助于将烛条索引转换为其类型。

//---
   for(int i=0;i<total_patterns;i++)
     {
      StringReplace(total_combination[i],"[","");
      StringReplace(total_combination[i],"]","");
      if(m_pattern_size>1)
        {
         ushort sep=StringGetCharacter(",",0);
         StringSplit(total_combination[i],sep,elements);
        }
      ZeroMemory(ratings[i]);
      m_pattern_total[i]=0;
      if(m_pattern_size==1)
         IndexToPatternType(cand1[i],(int)total_combination[i]);
      else if(m_pattern_size==2)
        {
         IndexToPatternType(cand1[i],(int)elements[0]);
         IndexToPatternType(cand2[i],(int)elements[1]);
        }
      else if(m_pattern_size==3)
        {
         IndexToPatternType(cand1[i],(int)elements[0]);
         IndexToPatternType(cand2[i],(int)elements[1]);
         IndexToPatternType(cand3[i],(int)elements[2]);
        }
     }

循环遍历所生成形态的每个字符串值,删除方括号,基于分隔符 “,” 应用 StringSplit(),并向 elements[] 数组添加信息。 请注意,此过程适用于由超过 1 根烛条组成的形态,因为对于一根烛条,不会有逗号,我们只需要删除方括号。 现在我们考察 IndexToPatternType() 方法,我们输入 CANDLE_STRUCTURE 来填充和处理所生成形态数组中的数据。

void CProgram::IndexToPatternType(CANDLE_STRUCTURE &res,const int index)
  {
//--- 长烛 - 多头
   if(index==1)
     {
      res.bull=true;
      res.type=CAND_LONG;
     }
//--- 长烛 - 空头
   else if(index==2)
     {
      res.bull=false;
      res.type=CAND_LONG;
     }
//--- 短烛 - 多头
   else if(index==3)
     {
      res.bull=true;
      res.type=CAND_SHORT;
     }
//--- 短烛 - 空头
   else if(index==4)
     {
      res.bull=false;
      res.type=CAND_SHORT;
     }
//--- 尖顶 - 多头
   else if(index==5)
     {
      res.bull=true;
      res.type=CAND_SPIN_TOP;
     }
//--- 尖顶 - 空头
   else if(index==6)
     {
      res.bull=false;
      res.type=CAND_SPIN_TOP;
     }
//--- 十字星
   else if(index==7)
     {
      res.bull=true;
      res.type=CAND_DOJI;
     }
//--- Marubozu - 多头
   else if(index==8)
     {
      res.bull=true;
      res.type=CAND_MARIBOZU;
     }
//--- Marubozu - 空头
   else if(index==9)
     {
      res.bull=false;
      res.type=CAND_MARIBOZU;
     }
//--- 锤子 - 多头
   else if(index==10)
     {
      res.bull=true;
      res.type=CAND_HAMMER;
     }
//--- 锤子 - 空头
   else if(index==11)
     {
      res.bull=false;
      res.type=CAND_HAMMER;
     }
  }

根据形态大小,填充一个、两个或三个 CANDLE_STRUCTURE:cand1,cand2,cand3。 参见以下代码:

if(m_pattern_size==1)
         IndexToPatternType(cand1[i],(int)total_combination[i]);
      else if(m_pattern_size==2)
        {
         IndexToPatternType(cand1[i],(int)elements[0]);
         IndexToPatternType(cand2[i],(int)elements[1]);
        }
      else if(m_pattern_size==3)
        {
         IndexToPatternType(cand1[i],(int)elements[0]);
         IndexToPatternType(cand2[i],(int)elements[1]);
         IndexToPatternType(cand3[i],(int)elements[2]);
        }

无论图表上的形态大小如何,都可以立即分析所需数量的烛条。 

对于单烛条形态,在每种特定情况下,只有一根烛条用于分析和计算,而整个集合将用于 2 烛条和 3 烛条形态。 例如,若样本范围为 2000 烛条,则对于 1 烛条形态,该集合将由第 2000 根烛条组成。 若是两根烛条,集合由第 2000 根和第 1999 根组成,依此类推。 

接下来,我们考察 GetPatternType() 方法的以下片段,该方法负责查找图表上的每个生成的形态。

//---
   for(int i=m_range_total2;i>5;i--)
     {
      if(m_pattern_size==1)
        {
         //--- 获取当前的烛条类型
         GetCandleType(symbol,cur_cand,m_timeframe2,i);                                         // Current candlestick
         //---
         for(int j=0;j<total_patterns;j++)
           {
            if(cur_cand.type==cand1[j].type && cur_cand.bull==cand1[j].bull)
              {
               m_pattern_total[j]++;
               GetCategory(symbol,i-3,ratings[j],m_timeframe2);
              }
           }
        }
      else if(m_pattern_size==2)
        {
         //--- 获取当前的烛条类型
         GetCandleType(symbol,prev_cand,m_timeframe2,i);                                        // 前根烛条
         GetCandleType(symbol,cur_cand,m_timeframe2,i-1);                                       // 当前烛条
         //---
         for(int j=0;j<total_patterns;j++)
           {
            if(cur_cand.type==cand1[j].type && cur_cand.bull==cand1[j].bull && 
               prev_cand.type==cand2[j].type && prev_cand.bull==cand2[j].bull)
              {
               m_pattern_total[j]++;
               GetCategory(symbol,i-4,ratings[j],m_timeframe2);
              }
           }
        }
      else if(m_pattern_size==3)
        {
         //--- 获取当前的烛条类型
         GetCandleType(symbol,prev_cand2,m_timeframe2,i);                                       // 前根烛条
         GetCandleType(symbol,prev_cand,m_timeframe2,i-1);                                      // 前根烛条
         GetCandleType(symbol,cur_cand,m_timeframe2,i-2);                                       // 当前烛条
         //---
         for(int j=0;j<total_patterns;j++)
           {
            if(cur_cand.type==cand1[j].type && cur_cand.bull==cand1[j].bull && 
               prev_cand.type==cand2[j].type && prev_cand.bull==cand2[j].bull && 
               prev_cand2.type==cand3[j].type && prev_cand2.bull==cand3[j].bull)
              {
               m_pattern_total[j]++;
               GetCategory(symbol,i-5,ratings[j],m_timeframe2);
              }
           }
        }
     }

正如我们在上面的代码中看到的那样,循环处于最开始,停在了第六根烛条。 为什么? 在第一篇文章中我们提到,为了分析形态效率,我们需要判定形态形成后价格的走向,并找出形态预测的内容,预测频率和概率是多少。 为此评估,我们要在形态之后三根烛条内实施价格走势分析。 当前的零号烛条不参与分析,因为它尚未完成。 

图例 9 形态效率评估的计算。 

三烛条形态如图例 9 所示。 为了评估它的效率,除了零号烛条之外我们还需要三根烛条。 所以,为了满足当前形态的这些条件,形态内第一根烛条的的最小索引可以为 6。 

在搜索形态之后,计算其数量,并按 A、B、C 类别进行评估,我们需要处理收到数据并将结果添加到表格中。 这些计算在 CoefCalculation() 方法中执行。

//---
   for(int i=0;i<total_patterns;i++)
      CoefCalculation(m_table2,i,ratings[i],m_pattern_total[i]);

 方法的参数是:

  • m_table2 — 指向每个所生成形态计算结果的表格链接。
  • i — 表格所在行。
  • ratings[i] — RATING_SET 结构数组,其中包含每个形态基于评级的一组类别。
  • m_pattern_total[i] — 包含每种类型已发现形态数量的数组。

我们更详细地考察该方法。

//+------------------------------------------------------------------+
//| 计算效率评估系数                                                    |
//+------------------------------------------------------------------+
bool CProgram::CoefCalculation(CTable &table,const int row,RATING_SET &rate,int found)
  {
   double p1,p2,k1,k2;
   int sum1=0,sum2=0;
   sum1=rate.a_uptrend+rate.b_uptrend+rate.c_uptrend;
   sum2=rate.a_dntrend+rate.b_dntrend+rate.c_dntrend;
//---
   p1=(found>0)?NormalizeDouble((double)sum1/found*100,2):0;
   p2=(found>0)?NormalizeDouble((double)sum2/found*100,2):0;
   k1=(found>0)?NormalizeDouble((m_k1*rate.a_uptrend+m_k2*rate.b_uptrend+m_k3*rate.c_uptrend)/found,3):0;
   k2=(found>0)?NormalizeDouble((m_k1*rate.a_dntrend+m_k2*rate.b_dntrend+m_k3*rate.c_dntrend)/found,3):0;

   table.SetValue(1,row,(string)found);
   table.SetValue(2,row,(string)((double)found/m_range_total2*100),2);
   table.SetValue(3,row,(string)p1,2);
   table.SetValue(4,row,(string)p2,2);
   table.SetValue(5,row,(string)k1,2);
   table.SetValue(6,row,(string)k2,2);
//--- 更新表格
   table.Update(true);
   table.GetScrollVPointer().Update(true);
   table.GetScrollHPointer().Update(true);
   return(true);
  }

从方法实现中可以看出,由于使用了结构,我们可以清晰地看到如何计算形态分析系数。

演示应用程序操作

作为示例,我们将使用不同的参数测试一些生成的形态。

步骤 1. 选择简单烛条模型。

首先,我们需要在用过的烛条部分选择所有可能的简单烛条模型,设置 “有反复”,并将 “形态中的烛条数量” 设为一。 以点数为单位的阈值趋势值将设置为 200。 这是设置:

图例 10 用于分析所生成形态的第一个设置步骤。

现在导航到 “AutoSearch” 选项卡,在搜索框中键入 Major,然后单击搜索。 然后将时间帧设置为 Н1,并选择货币对 GBPUSD。 以下是测试结果。

图例 11 测试结果生成一根烛条形态。

选择六种最常见的烛条类型。 这些是降序 1,2,5,6,3,4。

步骤 2. 测试双烛条形态。

现在基于简单类型形成两根烛条形态。 导航到 “设置” 选项卡,然后取消曾选中的 7 到 11 的框。 这次我们设置 “无反复” 模式,并将 “形态中的蜡烛数” 设置为 2。 上述配置如图例 12 所示。

图例 12 测试生成的双烛条形态的配置。  

导航回到自动搜索,并点击 GBPUSD。 按选择烛条生成各种组合,并提供其评级。 但如果 “阈值趋势值” 较低,特别是在更高的时间帧内,价格走势结果通常几乎相同。 通过增加阈值,可以为形态搜索设置更严格的条件。 例如,下面是阈值等于 400 的结果:

图例 13 趋势阈值增加的测试结果。

在获得的结果中,我试图在一个方向上找到一个大的价格走势,而在反方向上找到若干较少的走势。 从图例 13 中可以看出,这种状况遇到了两次:[1,4] 和 [2,6]。 这些形态是长烛(多头)— 短烛(空头)和长烛(空头)— 尖顶(空头)。 第二种形态变体是最优选择,因为其出现的频率几乎高出一倍。

现在我们测试三烛条形态。 有了所有可能的简单模型选项,我们得到了太多的形态变体,所以我们将只使用 4 种类型,这在以前的测试中经常遇到 — 1,2,5,6。 正确配置的 “设置” 选项卡如下所示:

图例 14 测试生成的三烛条形态的配置。

阈值趋势值仍然等于 400。 然后打开 “AutoSearch” 选项卡,再次单击 GBPUSD。 这里使用了选择表现良好形态的相同原则:在一个方向上的价格走势比在反方向上超出许多。 这可以从效率系数中看出。 例如,连续两次我遇到非常有趣的结果,具有非常好的系数和概率参数。

图例 15 产生的三烛条形态测试结果。

这些形态是 [2,5,2] 和下一个 [2,5,5]:长烛(空头)— 尖顶(多头)— 长烛(空头)和长烛(空头)— 尖顶(多头)— 尖顶(多头)。 第一个烛条形态显示出上型趋势的高概率,和巨大的效率系数。 第二个则具有良好的单向概率,但效率系数略低。 

大量的参数组合可以提供其他有趣的结果。 测试时,建议不要同时使用所有 11 种烛条类型,因为数据处理可能需要很长时间。 所分析形态的最大可能组合等于 1463,未考虑样本范围,时间帧,趋势阈值和简单烛条类型的个别设置。 如此大量的分析需要花费很多时间。

结束语

下面附带的存档包含所有上述文件,并按照文件夹中的正确位置排布。 若要正常操作,您只需将 MQL5 文件夹保存到终端文件夹中。

本文中用到的程序

#
 名称
类型
秒数
1
PatternAnalyzer.mq5 图形界面
 用于分析烛条形态的工具栏
2 MainWindow.mqh 代码库  GUI 函数库
3 Program.mqh 代码库  用于创建界面和计算元素的方法库

全部回复

0/140

达人推荐

量化课程

    移动端课程