信号计算器

Vladimir Karputov | 10 五月, 2016

目录


简介

信号订阅者最常问到的问题就是: "我能订阅 NNN 信号吗?复制到我交易账户仓位的交易量是多少呢?"本文将帮助创建信号计算器 — 一个信号订阅者的好帮手。文章中还提供了信号计算器的简要使用手册。

真正的计算器是一个基于CDialog类的面板,在面板上使用了以下元件:

信号计算器是基于终端中可用的信号库来进行工作的,这种方法可以保证在信号和您的交易账户之间有最好的兼容性,感谢终端提供了用于根据某些特征进行信号过滤的系统。过滤器的主要功能是把复制效果不佳的信号隐藏或者删除那些无法复制的信号。

本文中提供的计算器可以从MetaTrader 5 和 MetaTrader 4市场免费下载:


1. 使用的限制

应该明确理解的是,只有与订阅者交易账户高度兼容的信号才在终端的"信号"页面显示,换句话说,所有只来自在展示中提供的信号将不会出现在终端中,另外,在终端中切换交易服务器(交易账户), 每次的信号列表可能也会有所不同。


2. 信号计算器的使用手册

2.1. 开始

为了使信号计算器能够正常工作,需要在终端中有最新的交易信号库,所以,我们需要在"工具箱"窗口中激活"信号"页面(点击"信号"页面):


图 1. 激活"信号"页面 

信号库的任何变动都将在三到五秒内更新,

"信号"页面应该在连接交易账户之后激活,或者在连接另一个交易账户时激活。


2.2. 用户界面

计算器的界面包含以下元件:


图 2. 计算器的界面 

  • Trading account's balance(交易账户余额) — 当前所连接的交易账户的余额 — 可编辑栏位。在把信号计算器附加到图表之后或者改变了图表的时段,"Balance(余额)"栏位就包含了那个时刻它所连接的交易账户的余额。这个栏位只允许输入0到9的数字。
  • Trading account's currency(交易账户币别) — 当前所连接的交易账户的币别 — 一个常用币别的下拉列表。在把信号计算器附加到图表之后或者改变了图表的时段,计算器会尝试在列表中找到交易账户的币别名称。如果没有找到对应的币别,就会选择"USD"作为默认值。
  • Trading account's leverage(交易账户杠杆) — 当前所连接的交易账户的杠杆 - 一个常用杠杆的下拉列表。在把信号计算器附加到图表之后或者改变了图表的时段,计算器会尝试在列表中寻找当前连接账户的杠杆。如果没有找到对应值, 就使用"1:100"的杠杆作为默认值。
  • Deposit load when copying a signal(复制信号时的入金比例) — 一个复制信号使用入金比例的下拉列表,对应了终端菜单中的"工具" - "选项" - "信号"中的数值,在把信号计算器附加到图表或者改变了图表的时段时,计算器会寻找交易账户的使用入金比例。如果没有找到对应值,就会使用"95%"作为默认使用入金的比例。
  • Table of signals available in the terminal(终端中可用的信号表格) — 一个包含"信号"页面所包含信号的表格。表格中的内容根据"Copy ratio(复制比例)"列进行降序排列。表格中的列:
    • "Growth, %(增长百分率)" 列 — 通过交易操作账户增长的百分比。
    • "Signal(信号)" 列 — 交易信号的名称。
    • "Funds(资金)" 列 — 信号提供者账户的自有资金数量,可以考虑其可信度。
    • "Currency(币别)" 列 — 信号提供者交易账户的币别。
    • "Leverage(杠杆)" 列 — 信号提供者交易账户的杠杆。
    • "Price, $(价格)" 列 — 交易信号的价格,单位为美元。
    • "Copy ratio(复制比例)" 列 — 根据选择的设置计算得到的每个信号的复制比例:"Trading account's balance(交易账户余额)", "Trading account's currency(交易账户币别)", "Trading account's leverage(交易账户杠杆)" 和 "Deposit load when copying a signal(复制信号的入金百分比)"。
    • The "最小存款 *" 列 — 当使用95%资金时,达到1:1复制所需的存款数量。说得更清楚一些,这就是在选择使用"95%"的存款的条件下,把信号提供者的交易按照1:1复制到您的交易账户所需要的存款数量。
  • Detailed calculation of copy ratio (复制比例的计算细节) — 计算所选信号复制比例的每一步骤的计算过程。
    • К1 — 您的交易账户与信号提供者交易账户币别的比例。
    • К2 — 您的交易账户与信号提供者交易账户余额的比例。
    • К3 — 使用存款的比例,把百分数转换为比例。
    • К4 — 杠杆差异的修正比例。
    • К — 最终比例,它是用比例相乘 К1*К2*К3*К4 计算所得。
    • 最终复制比例 — 使用多步算法对К比例进行取整

2.3. 获取复制比例

信号列表的"Copy ratio"列显示了根据所选信号设置计算的复制比例: "Trading account's balance(余额)", "Trading account's currency(币别)", "Trading account's leverage(杠杆)" 和 "Deposit load when copying a signal(资金比例)", 

如果您想要的话,这些设置可以修改: "Trading account's balance(余额)", "Trading account's currency(币别)", "Trading account's leverage(杠杆)" 或者 "Deposit load when copying a signal(资金比例)". 修改这些设置中的任意一个,都会重新计算复制比例并在表格中更新,并且不能保证交易信号还保留在原位,因为之后会根据"最小存款*"列进行降序排列。这样您就可以看到,交易信号将根据使用的不同设置而改变其实时的复制比例。

2.4. 复制比例的计算细节

为了取得指定信号的复制比例的详细计算过程,您必须在信号表格的对应行中选择您所感兴趣的信号(第一步),所选信号复制比例的计算细节会立即在信号表格下方显示出来。(第二步):

 

图 3. 复制比例计算细节  

2.5. 无法映射

在"Trading account's currency(交易账户币别)"下拉列表中选择了不同的币别之后, 信号计算器会在"市场报价"窗口尝试找到您交易账户的币别(或者在"Trading account's currency"中选择的币别)以便计算复制比例。例如,您的交易账户币别为"USD", 而信号提供者交易账户的币别是"EUR", 在这种情况下,计算器会尝试在"市场报价"窗口寻找"USDEUR" 和 "EURUSD"的交易品种。如果交易品种没有找到,终端的"专家"页面会显示错误消息。

以下例子中显示了在"Trading account's currency"的下拉列表中选择了"SGD"的错误消息:

Сalculator for signals (EURUSD,M5)      Error find symbols: (Account currency SGD, Signal currency RUB)
Сalculator for signals (EURUSD,M5)      Error find symbols: (Account currency SGD, Signal currency EUR)
Сalculator for signals (EURUSD,M5)      Error find symbols: (Account currency SGD, Signal currency EUR)

这个消息是说明,在"市场报价"窗口中没有"SGDRUB", "SGDEUR", "RUBSGD", "EURSGD" 等交易品种。如果出现这种情况让我们这样做: 我们会在"市场报价"窗口中寻找任何包含"SGD"的交易品种,在"市场报价"窗口中点击"+ 点击添加...":


图 4.  在"市场报价"窗口中点击添加命令   

并在打开的栏位中输入"SGD":


图 5. "市场报价"在名称中包含"SGD"的可用交易品种列表   

您可以看到,"市场报价"已经有了"USDSGD"的交易品种,还有一个 — "SGDJPY"可以添加,但是没有"SGDRUB", "SGDEUR", "RUBSGD", "EURSGD"等交易品种。 

无法进行映射,信号表格中的"Copy ratio "将显示"n/d" (no data,没有数据)。

信号计算器的简短视频:



3. 开发信号计算器

3.1. 界面设计

信号计算器的控件安排如下:

控件的排布  

图 6. 控件的排布 

附加的"Сalculator for signals Dialog.mqh"文件就是负责控件的排布,大小和创建的,控件的主要尺寸,边缘设置是使用宏替换区块的:

//+------------------------------------------------------------------+
//| 定义                               |
//+------------------------------------------------------------------+
//--- 缩进和间隙
#define INDENT_LEFT                         (11)      // 左边的缩进 (包含边框宽度)
#define INDENT_TOP                          (11)      // 顶端的缩进 (包含边框宽度)
#define CONTROLS_GAP_Y                      (5)       // 纵坐标上的间隙
//--- for combo boxes
#define COMBOBOX_WIDTH                      (60)      // 横坐标上的大小
#define COMBOBOX_HEIGHT                     (20)      // 纵坐标上的大小
//--- for list view
#define LIST_HEIGHT                         (102)     // 纵坐标上的大小
//--- for buttons
#define BUTTON_WIDTH                        (72)      // 横坐标上的大小
#define BUTTON_HEIGHT                       (20)      // 纵坐标上的大小
//--- for the indication area
#define EDIT_WIDTH                          (60)      // 横坐标上的大小
#define EDIT_HEIGHT                         (20)      // 纵坐标上的大小

面板上的控件一共分成五行:

  1. 第一行 — Label1, Edit1, ComboBox1, Label2, ComboBox2, Label3, ComboBox2
  2. 第二行 — Label 4
  3. 第三行 — 按钮 Button1- Button8
  4. 第四行 — 新控件 — TableListView1
  5. 第五行 — BmpButton1 对象,它是基于CCanvans以显示位图的方式来显示控件的。
很重要的一点是,我们要记住所有控件对象都是位于对话框的面板上(主面板就在此创建):
//+------------------------------------------------------------------+
//| 创建                                |
//+------------------------------------------------------------------+
bool CoSDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//---
   m_error=true;
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//---

这表示在创建之后,每个控件对象都要使用CDialog类的Add方法把它们加到客户区域中。以下是创建 Label4 图形对象的示例:

//+------------------------------------------------------------------+
//| 创建 "Signals" 标签                         |
//+------------------------------------------------------------------+
bool CoSDialog::CreateLabel4(void)
  {
//--- 坐标
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+COMBOBOX_HEIGHT+CONTROLS_GAP_Y;
   int x2=x1+100;
   int y2=y1+COMBOBOX_HEIGHT;
//--- 创建
   if(!m_label4.Create(m_chart_id,m_name+"Label4",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_label4.Text(m_languages.GetText(3)))
      return(false);
   if(!Add(m_label4))
      return(false);
//--- 成功
   return(true);
  }

首先,创建 Label4 对象:

   if(!m_label4.Create(m_chart_id,m_name+"Label4",m_subwin,x1,y1,x2,y2))
      return(false);

然后,必须把新创建的 Label4 对象使用Add方法,在退出函数之前加到客户区域中:

   if(!Add(m_label4))
      return(false);

一般来说,创建面板和其中的控件的过程可以使用创建信号计算器概要图的方式显示:

创建控件的概要图

图 7. 创建控件的概要图 

让我们总结一下. 一般来说,面板是按照以下计划创建的:

  • 创建面板 (CAppDialog::Create)
  • 按照以下顺序创建控件:
    • 创建元件 (element_N.Create)
    • 修改元件属性
    • 明确把元件加到面板上 (Add(elenemt_N))

3.2. 在面板上创建画布

画布是在 CoSDialog::CreateBmpButton1 中创建的,

首先, 主要步骤包含:

第一步: 创建一个与图表对象无关的图形资源(使用CCanvans类的Create方法)

第二步: 创建CBmpButton类型的m_bmp_button1控件 (基于 'Bitmap label' 对象的简单控件类)

第三步: 把我们的 m_bmp_button1 控件的画布设为位图文件状态(BmpOnName方法)

第四步: 把m_bmp_button1控件加到面板上( CDialog类的Add方法)

现在,是每一步的更多信息。

第一步

//--- 创建画布
   if(!m_canvas1.Create("Canvas1",x2,y2,COLOR_FORMAT_XRGB_NOALPHA))
     {
      Print("Error creating canvas: ",GetLastError());
      return(false);
     }
   m_canvas1.FontSet("Trebuchet MS",-100,FW_THIN);
   m_canvas1.Erase(ColorToARGB(C'0xF7,0xF7,0xF7',255));
   m_canvas1.Update(true);

请注意,在创建画布时(m_canvas1.Create)指定了x2宽度和y2高度 — 实际上,这些是图标的尺寸,要保持一样:

画布大小  

图 8. 画布大小 

其后,设置画布的字体 (m_canvas1.FontSet), 画布使用面板颜色填充 (m_canvas1.Erase) , 改变必须显示出来 (m_canvas1.Update)。

第二步.

//--- 创建
   if(!m_bmp_button1.Create(m_chart_id,m_name+"BmpButton1",m_subwin,x1,y1,x1+10,y1+10))
      return(false);

m_bmp_button1控件创建时的大小是10*10像素点,尺寸较小也没有关系,真正的坐标x1y1是关键的,它们是锚点。当控件大小适应图标大小时,这些坐标(x1; y1)是起作用的。

第三步.

//--- 设置 CBmpButton 控件位图文件的名称
   if(!m_bmp_button1.BmpOnName(m_canvas1.ResourceName()))
      return(false);

我们取得资源名称(m_canvas1.ResourceName())并把它在ON状态与位图相联系(m_bmp_button1.BmpOnName),在这一步中, m_bmp_button1控件会根据画布尺寸进行拉伸。

第四步.

请不要忘记在创建完控件后把它加到面板上: 

   if(!Add(m_bmp_button1))
      return(false);

3.3. 二维数组排序

排序是在"Сalculator for signals Dialog.mqh"文件的CoSDialog::InitStructurs方法中进行的, 

信号计算器中的表格数据是按照计算的指标 — 复制比例来排序的。在获得复制比例的过程中,包含了信号名称和计算的复制比例列表,不做排序的话看起来会这样:

Name Copy ratio
TestUSD 15.0
TradeLargeVolumes 120.0
Zeus PRO 15.0
CS Trading Co Beta Free Provider 1510.0
Mint blueberry ice cream 8.0
MT5Hedging 7.0
Forex Leos Trading 160.0
Hedge 1.0
Siksikawa 8770.0
Week H4 15.0
WGT Live Signal 45.0
Atrader 30.0
Trajecta Advisor FX491 30.0
MOsg style 6.0 

为了排序,我们已经使用了Vasiliy Sokolov建议的方案: 每个字符串必须由CObject表示,而整个表格使用 — CArrayObj。表格被称为线性表, 虚拟线性表的类(称为虚拟是因为它不包含可视的界面)放在"LineTable.mqh"文件中。

排序的结果在以下线性表中显示(例子中的表格第一行插入了一些内容)。首先,显示了插入线性表中的参数(Insert : ), 以下是迭代线性表中的所有元素 (row #):

name rate  min_deposit
 Insert : MyCorrelation EURUSD XAUUSD  7  133134.7143
 row #0: MyCorrelation EURUSD XAUUSD  7  133134.7143
 Insert : EA microPatience AvzPrecio v6 HG 10  7  133134.7143
 row #0: MyCorrelation EURUSD XAUUSD  7  133134.7143
 row #1: EA microPatience AvzPrecio v6 HG 10  7  133134.7143
 Insert : EURUSD Daytrade  170  5482.017647
 row #0: EURUSD Daytrade  170  5482.017647
 row #1: MyCorrelation EURUSD XAUUSD  7   133134.7143
 row #2: EA microPatience AvzPrecio v6 HG 10  7  133134.7143
 Insert : Exp TickSniper PRO FULL MT5  50  18638.86
 row #0: EURUSD Daytrade  170  5482.017647
 row #1: Exp TickSniper PRO FULL MT5  50  18638.86
 row #2: MyCorrelation EURUSD XAUUSD  7  133134.7143
 row #3: EA microPatience AvzPrecio v6 HG 10  7  133134.7143
 Insert : Example1  3  310647.6667
 row #0: EURUSD Daytrade  170  5482.017647
 row #1: Exp TickSniper PRO FULL MT5  50  18638.86
 row #2: MyCorrelation EURUSD XAUUSD  7  133134.7143
 row #3: EA microPatience AvzPrecio v6 HG 10  7  133134.7143
 row #4: Example1  3  310647.6667
 Insert : Big sale  80  11649.2875
 row #0: EURUSD Daytrade  170  5482.017647
 row #1: Big sale  80  11649.2875
 row #2: Exp TickSniper PRO FULL MT5  50  18638.86
 row #3: MyCorrelation EURUSD XAUUSD  7  133134.7143
 row #4: EA microPatience AvzPrecio v6 HG 10  7  133134.7143
 row #5: Example1  3  310647.6667

图片清晰地显示了在插入线性表之后,根据"rate"栏位进行了排序, 这正是我们所需的。在将来,知道了所有线性表中的控件都排序后,我们可以把它们的数值复制到新控件 — CTableListView 表格中, 并且确信数据已经进行了排序。

如果您想打印出排序的详细步骤并得到相同的表格,只需要在"Сalculator for signals Dialog.mqh"文件的CoSDialog::InitStructurs 方法中去掉注释

         else
           {
            min_deposit=AccInfo.balance/rate*100.0;
           }
         //Print("Insert : ",name,"; ",rate,"; ",min_deposit); 
         m_table.InsertSort(new CLineTable(name,rate,min_deposit));
         //for(int m=0;m<m_table.Total();m++)
         //  {
         //   CLineTable *line=m_table.At(m);
         //   Print("row #",m,": ",line.Text(),"; ",line.Number(),"; ",line.Number1());
         //  }
        }
      else PrintFormat("Error in call of SignalBaseSelect. Error code=%d",GetLastError());
     }
//---
   return(true);
  }

3.4. 新控件 — CTableListView 表格

为了显示信号,它们的特性以及计算的数据,我们将创建一个可视控件 — 一个可以显示一些行和列的表格。同时,如果行数超过了表格的高度,还需要出现垂直的滚动条。在做过搜索之后,我决定以 CListView 类的控件作为基础,然后进一步修改它。

CTableListView控件表格包含两个部分 — 一个用户可以看到和操作的可见部分,以及保存表格单元数据的不可见部分。

表格的可见部分由控件的大小规定的,它是由含坐标的长方形(x1; y1) 和 (x2; y2) 以及在Create方法中创建的元件来做到的:

   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,
                            const int y1,const int x2,const int y2,const uchar columns,const ushort &columns_size[]);

可见部分包含了CEdit控件: 

创建 CTableListView 表格的可见部分

图 9. 创建 CTableListView 表格的可见部分

可见部分的 CEdit 控件的创建和管理是通过动态数组指针 m_arr_rows  CArrayObj类型的对象来做的:

   CArrayObj         m_arr_rows;             // objects-rows (CEdit) 指针数组

(一篇关于指针使用的文章 — 在 MQL5 中使用对象指针)

m_arr_rows指针数组就像一个两层的俄罗斯娃娃那样工作,第一层把指针保存在row对象中,它保存了表格单元对象(CEdit元件)的指针:

可见对象的指针

图 10. 可见对象的指针 

不可见部分也是通过CArrayObj类实现的,二维数组m_arr_rows_str m_arr_rows_val是用于负责不可见部分的,

   CArrayObj         m_arr_rows_str;         // objects-rows (CArrayString) 的指针数组
   CArrayObj         m_arr_rows_val;         // objects-rows (CArrayLong) 的指针数组 

其中的文字和数值分别存储。

m_arr_rows_str 指针数组和m_arr_rows_val 的结构与m_arr_rows指针数组的结构类似, 所不同的就是分别使用了CArrayStringCArrayLong

3.4.1. 操作动态指针数组的例子

让我们根据创建对象(表格)的示例来分析动态指针数组的操作:

//+------------------------------------------------------------------+
//| 创建控件                               |
//+------------------------------------------------------------------+
bool CTableListView::Create(const long chart,const string name,const int subwin,const int x1,
                            const int y1,const int x2,const int y2,const uchar columns,const ushort &columns_size[])
  {
   m_columns=columns;
   ArrayResize(m_columns_size,m_columns);
   if(ArraySize(columns_size)!=m_columns)
      return(false);
   ArrayCopy(m_columns_size,columns_size,0,0,WHOLE_ARRAY);
   m_columns_size[0]-=1;
   m_columns_size[m_columns-1]-=1;
   int y=y2;
//--- 如果之前确定了可见的行数,调整垂直大小
   if(!TotalView((y2-y1)/m_item_height))
      y=m_item_height+y1+2*CONTROLS_BORDER_WIDTH;
//--- 检查可见行数
   if(m_total_view<1)
      return(false);
//--- 调用父类方法
   if(!CWndClient::Create(chart,name,subwin,x1,y1,x2,y))
      return(false);
//--- 设置
   if(!m_background.ColorBackground(CONTROLS_LIST_COLOR_BG))
      return(false);
   if(!m_background.ColorBorder(CONTROLS_LIST_COLOR_BORDER))
      return(false);
//--- 创建依赖的控件
   CArrayObj *m_arr_cells;
   for(int i=0;i<m_total_view;i++)
     {
      m_arr_cells=new CArrayObj;
      if(CheckPointer(m_arr_cells)==POINTER_INVALID)
         return(false);
      for(int j=0;j<m_columns;j++)
        {
         CEdit *m_cell;
         m_cell=new CEdit;
         if(CheckPointer(m_cell)==POINTER_INVALID)
            return(false);
         m_arr_cells.Add(m_cell);
        }
      m_arr_rows.Add(m_arr_cells);
     }
//---
   for(int i=0;i<m_total_view;i++)
     {
      if(!CreateRow(i))
         return(false);
      if(m_height_variable && i>0)
        {
         // m_rows[i].Hide(); ///
         CArrayObj *m_arr_cells_i=m_arr_rows.At(i);
         if(CheckPointer(m_arr_cells_i)==POINTER_INVALID)
            return(false);
         for(int j=0;j<m_arr_cells_i.Total();j++)
           {
            CEdit *m_cell=m_arr_cells_i.At(j);
            if(CheckPointer(m_cell)==POINTER_INVALID)
               return(false);
            if(!m_cell.Hide())
               return(false);
           }
        }
     }
//--- 成功
   return(true);
  }

以下代码块

//--- 如果之前确定了可见的行数,调整垂直大小
   if(!TotalView((y2-y1)/m_item_height))
      y=m_item_height+y1+2*CONTROLS_BORDER_WIDTH;

是用于调用TotalView以确定可见行数的,该数值保存在m_total_view变量中,

另外, 在for(int i=0;i<m_total_view;i++)循环中创建了m_arr_cells行, 而这些行在for(int j=0;j<m_columns;j++)循环中填充了m_cell个单元:

//--- 创建依赖的控件
   CArrayObj *m_arr_cells;
   for(int i=0;i<m_total_view;i++)
     {
      m_arr_cells=new CArrayObj;
      if(CheckPointer(m_arr_cells)==POINTER_INVALID)
         return(false);
      for(int j=0;j<m_columns;j++)
        {
         CEdit *m_cell;
         m_cell=new CEdit;
         if(CheckPointer(m_cell)==POINTER_INVALID)
            return(false);
         m_arr_cells.Add(m_cell);
        }
      m_arr_rows.Add(m_arr_cells);
     }
//---

在完全经过for(int j=0;j<m_columns;j++)循环后,每个填充的行都加到了主数组 — m_arr_rows中:

      m_arr_rows.Add(m_arr_cells);

通过这种方法,在完全通过for(int i=0;i<m_total_view;i++)循环后,我们就有了填充好的指标数组m_arr_rows,在它的结构中对应了表格的可见部分 (参见图 9)。

在第二个循环for(int i=0;i<m_total_view;i++)中填充了指针数组之后, 通过调用CreateRow使表格可见 (创建表格的可见部分):

//---
   for(int i=0;i<m_total_view;i++)
     {
      if(!CreateRow(i))
         return(false);
      .
      .
      .
     }
//--- 成功
   return(true);

CreateRow 方法:

//+------------------------------------------------------------------+
//| 创建 "row(行)"                            |
//+------------------------------------------------------------------+
bool CTableListView::CreateRow(const int index)
  {
   .
   .
   .
//--- 创建
   CArrayObj *m_arr_cells=m_arr_rows.At(index);
   if(CheckPointer(m_arr_cells)==POINTER_INVALID)
      return(false);
   for(int i=0;i<m_arr_cells.Total();i++)
     {
      CEdit *m_cell=m_arr_cells.At(i);
      if(CheckPointer(m_cell)==POINTER_INVALID)
         return(false);
      x1+=x2;
      x2=m_columns_size[i];
      if(!m_cell.Create(m_chart_id,m_name+"_"+IntegerToString(index)+"_"+IntegerToString(i),
         m_subwin,x1,y1,x1+x2,y2))
         return(false);
      if(!m_cell.Text(""))
         return(false);
      if(!m_cell.ReadOnly(true))
         return(false);
      if(!Add(m_cell))
         return(false);
     }
   .
   .
   .
   return(true);
  }

我们得到了一个指向m_arr_cells元件的指针(m_arr_cellsCArrayObj类型的), 在代码块中把它从m_arr_rows指针数组中放到index位置。m_arr_cells 元件实际上就是row表格中的一行字 (参见图 10). 我们使用CArrayObjAt方法:

   .
//--- 创建
   CArrayObj *m_arr_cells=m_arr_rows.At(index);
   if(CheckPointer(m_arr_cells)==POINTER_INVALID)
      return(false);
   for(int i=0;i<m_arr_cells.Total();i++)

然后,我们在 for(int i=0;i<m_arr_cells.Total();i++)循环中操作m_arr_cells控件(指针数组): 一个m_cell元件(m_cellCEdit类型的), 把它放到i位置, 它是从m_arr_cells指针数组中取得的:

      CEdit *m_cell=m_arr_cells.At(i);
      if(CheckPointer(m_cell)==POINTER_INVALID)
         return(false);

然后,我们使用唯一名称创建单元 (CEdit类的控件):

      if(!m_cell.Create(m_chart_id,m_name+"_"+IntegerToString(index)+"_"+IntegerToString(i),
         m_subwin,x1,y1,x1+x2,y2))
         return(false);

修改所创建控件的一些属性(删除所有控件的文字并使它不可编辑):

      if(!m_cell.Text(""))
         return(false);
      if(!m_cell.ReadOnly(true))
         return(false);

然后最后一步 — 我们把新创建的控件加到面板上:

      if(!Add(m_cell))
         return(false);
     }


 

结论

我希望您在选择信号时会觉得信号计算器对您有用,最重要的是,它使得您可以看到,在改变您交易账户的存款和杠杆大小时复制比例将会有怎样的变化。