English Русский Español Deutsch 日本語 Português
如何使用 EA 遵照您的规则拷贝信号?

如何使用 EA 遵照您的规则拷贝信号?

MetaTrader 5示例 | 15 九月 2016, 10:51
3 996 0
Vladimir Karputov
Vladimir Karputov

内容目录

 

风险警示

使用此方法之前, 您要依据以往的常识, 因为增加拷贝比率的同时意味着风险增加。

当跟单时, 系统周期性检查订阅者账户内仓位是否与提供者的匹配。如果检测到不匹配 (如, 只有部分仓位被拷贝), 则系统尝试消除并复制缺失的仓位。不像初始同步, 不检查提供者的初始总浮盈。如果订阅者已经开始跟单, 他们将会跟随提供者的交易策略来最大可能的拓展。一些不可能拷贝的仓位, 将会被忽略。

如果系统检测到订阅者账户内存在并非基于信号的仓位, 它会提示将它们平仓, 或者根据 "无需确认同步仓位" 设置来自动将它们平仓。

只有当提供者用最小手数交易, 在设置中将本金负载设为 95% (本金最大利用率), 同时交易拷贝比率依旧很小时, 用此方法增加信号拷贝的比率才有意义。您可以在文章 信号计算器 的帮助下, 在您订阅喜欢的信号之前了解今后的拷贝比率。

本文中表述的拷贝交易信号助理程序可以从市场中免费下载:


1. 准备

在您开始之前, 请参见教程视频:

  1. 选择交易信号
  2. 订阅信号
  3. 租用虚拟主机
  4. 迁移订阅和智能交易程序到虚拟主机


1.1. 拷贝机的思路

拷贝机的操作位于虚拟主机上的 信号 订阅账户里。它的主要目的是按照指定倍数增加来自信号服务的开仓手数。图例. 1 示意加载到对冲账户中的拷贝机思路:


图例. 1. 对冲账户中的拷贝机思路

图例. 2 展示加载到净持仓交易系统账户中的拷贝机操作思路:

 

图例. 净持仓账户里的拷贝机思路 

因此, 一旦信号服务在订阅账户里成功执行了一笔交易, 拷贝机立即执行一笔交易, 其交易量由此公式确定:

(信号服务所执行交易的成交量) * "Deal multiply by"

参数 "Deal multiply by" 负责增加成交比率, 它可在输入参数里设定:

输入参数

图例. 3. 输入参数 

当在 MetaTrader 5 的净持仓交易账户里工作时, 拷贝机的行动将会令已拷贝的仓位成交量增加。当在 MetaTrader 5 的对冲账户里工作时, 拷贝机的行动将会导致按照增加的交易量开新仓。以下是当增加交易量的 "Deal multiply by" 参数设为 5 时, 拷贝机在 MetaTrader 5 的净持仓账户和对冲账户里的操作示例:

信号服务的动作 拷贝机在
对冲账户里的动作
拷贝机在
净持仓账户里的动作
拷贝交易 BUY EURUSD 0.01 手 开新仓 BUY EURUSD 0.05 手 — 因此, 订阅者有两笔持仓 BUY EURUSD 0.01 手和 BUY EURUSD 0.06 手 已开成交 BUY EURUSD 0.05 手 — 因此, 订阅者有一笔持仓 BUY EURUSD 0.06 手

 

2. 信号服务的功能

2.1. 友方或是敌方

由于拷贝机监视所有信号服务执行的成交, 有必要了解信号服务操作的某些功能。例如, 该服务仅维护 "友方" 仓位 — 那些通过服务拷贝的。

当订阅者一侧成功开仓时, 信号服务将信号名称写入拷贝的成交备注里, 并在成交的魔幻数字字段里写入独有标识符。

在之后的订阅同步时, 此标识符用来识别通过信号服务开仓的成交。

这是 BUY 0.01 EURUSD 的备注, 成交拷贝自 "Test3443431" 信号。在 "工具箱" 窗口里的 "历史" 栏中, 将鼠标悬浮在拷贝的成交上即可查看它:

 拷贝成交的备注 ("历史" 栏)

图例. 4. 拷贝成交的备注 ("历史" 栏)

此处:

  1. "#69801797" — 成交号;
  2. "Test3443431" — 此拷贝成交的信号源名称;
  3. "361104976048745684" — 魔幻数字 — 成交标识符。

在 "交易" 栏里可以看出, 订阅者的当前持仓有相同的描述, 但没有单号:

拷贝成交的备注 ("交易" 栏)

图例. 5. 拷贝成交的备注 ("交易" 栏)

换言之, 信号服务利用订阅者一端成交单的魔幻数字认定仓位。认定仓位将被确定为 "友方"。信号服务只会改变 "友方" 仓位的成交量或将之平仓。

未认定仓位将被标识为 "敌方"。信号服务不花费力量在这些仓位上。

2.2. 是否取代魔幻数字?

MetaTrader 5 终端允许订阅者连接对冲和净持仓两种类型账户。根据连接账户类型, 拷贝机替换已拷贝仓位魔幻数字的行为也将大有不同。为了回答这个问题, 让我们参考下图:


图例. 6. 净持仓账户, 无需替换魔幻数字 

如其所示, 在净持仓账户里拷贝机加仓而无需替换魔幻数字 — 这会导致仓位变成信号服务的 "敌方"。这就是, 拷贝机的操作无需替换魔幻数字是一个错误。这意味着在一个净持仓账户里, 当拷贝机加仓时, 必须总是替换魔幻数字。但在对冲账户里操作时所有东西都是相反的: 当加仓时, 拷贝机必须用它自己替换魔幻数字。

2.3. 如果提供者将其平仓, "敌方" 仓位将会发生什么?

在对冲账户里的示例: 在订阅者账户里开一笔 BUY 0.01 EURUSD 仓位。之后订阅者决定干预服务的操作并开一笔 BUY 0.01 EURUSD。在最近的一次信号同步时, 服务会生成此错误:

Signal  '3447880': local positions do not correspond to signal provider [#85639429 buy 0.04 EURUSD 1.11697]


就是, 由订阅者手工开仓的 BUY 0.01 EURUSD, 已被标识为信号服务的 "敌方"。现在, 我们来看看当提供者将其平仓时会发生什么: 信号服务同样将 "友方" 平仓, 但手工的开仓 BUY 0.01 EURUSD 依旧遗留在终端里。顺言之, 在信号服务规则里已明示, 对于人工干预操作置之不理:

IV. 订阅信号

   20. 在已订阅信号的账户中自行执行交易, 将构成干扰并会导致不可预测的结果。


3. 如何检测成交出现

选择识别成交出现的方法, 一方面, 影响拷贝的响应速率, 其它方面 - 可能会导致额外的财务成本, 比如依照增加的交易量开单却出现错误时的点差。

应当记住, 我们已订阅信号, 这意味着信号提供者的交易事件由 信号 服务所监视。若有必要, 该服务将会开仓或平仓。所有开仓/平仓导致订阅者的交易账户发生变化。这种变化应被跟踪, 且 OnTradeTransaction() 事件将会通报有关它的信息。

3.1. 使用 OnTradeTransaction() 跟踪

为什么选择 OnTradeTransaction() 而非 OnTrade()?因为 OnTradeTransaction() 包含十分有用的信息 — 交易事务类型。在 所有可能的事务类型 当中只对其一感兴趣:

TRADE_TRANSACTION_DEAL_ADD — 在历史中添加成交。它作为订单执行或账户财务操作的结果而被执行。

即是, 拷贝机等待成交被添加到历史 (保证在订阅者帐户里的交易成功操作), 且之后才会开始处理情况。 


4. 何时可以订阅以及何时不可

只有在双方的账户仓位认定系统相同时才可成功订阅:

订阅 结果
提供者 — 净持仓账户, 订阅者 — 对冲账户

对冲账户系统尝试订阅提供者的净持仓账户系统失败。 

2016.05.11 11:15:07.086 Signal  '*******': subscribe to signal [*****] started
2016.05.11 11:15:07.141 Signal  '*******': subscription/renewal prohibited


如果提供者为净持仓账户而订阅者为对冲账户, 信号不可订阅。

提供者 — 对冲账户, 订阅者 — 净持仓账户

净持仓账户系统尝试订阅提供者的对冲账户系统失败。 

2016.05.11 11:39:54.506 Signal  '*******': subscribe to signal [******] started
2016.05.11 11:39:54.560 Signal  '*******': subscription/renewal prohibited


如果提供者为对冲账户而订阅者为净持仓账户, 信号不可订阅。

提供者 — 对冲账户, 订阅者 — 对冲账户 订阅成功。
提供者 — 净持仓账户, 订阅者 — 净持仓账户 订阅成功。

5. 保存关于拷贝的信息

哪里是保存拷贝执行信息的最佳位置?以及一个更全局的问题 — 是否有必要保存这些信息?在这个版本的拷贝机中, 信息保存在 结构 里。结构声明:

//+------------------------------------------------------------------+
//| 终端的仓位聚合                                                    |
//| 以及拷贝机的仓位结构                                              |
//+------------------------------------------------------------------+
struct correlation
  {
   long              POSITION_IDENTIFIER_terminal; // 仓位识别符 (终端)
   //+------------------------------------------------------------------+
   //| 仓位标识符是在开新仓时分配的独有数字                                |
   //| 且在持仓的完整生命周期内                                           |
   //| 不可更改。                                                        |
   //| 仓位翻转不会改变其标识符。                                         |
   //+------------------------------------------------------------------+
   double            POSITION_VOLUME_terminal;     // 由信号服务开仓的成交量
   long              POSITION_IDENTIFIER_copier;   // 仓位标识符 (拷贝机)
   //+------------------------------------------------------------------+
   //| 仓位标识符是在开新仓时分配的独有数字                                |
   //| 且在持仓的完整生命周期内                                           |
   //| 不可更改。                                                        |
   //| 仓位翻转不会改变其标识符。                                         |
   //+------------------------------------------------------------------+
   ulong             DEAL_ticket;                  // 成交单号, 如果成交已执行。
  };

结构只包含四个元素:  

  1. "POSITION_IDENTIFIER_terminal" — 此结构保存由终端 (信号服务) 打开的仓位标识符
  2. "POSITION_VOLUME_terminal" — 保存由终端 (信号服务) 打开的仓位成交量
  3. "POSITION_IDENTIFIER_copier" — 此元素保存由拷贝机打开的仓位标识符
  4. "DEAL_ticket" — 如果成交成功, 保存由拷贝机打开的成交单号

所有拷贝机执行的动作仅来自 OnTradeTransaction 函数, 且事务类型只有 TRADE_TRANSACTION_DEAL_ADD。当这些条件满足时, 它总是会寻找产生这种事务的成交, 并恢复其参数:

//+------------------------------------------------------------------+ 
//| TradeTransaction 函数                                            | 
//+------------------------------------------------------------------+ 
void OnTradeTransaction(const MqlTradeTransaction &trans,
                        const MqlTradeRequest &request,
                        const MqlTradeResult &result)
  {
//--- 获取事务类型的枚举值 
   ENUM_TRADE_TRANSACTION_TYPE type=trans.type;
//--- 如果事务是添加事务到历史的结果
   if(type==TRADE_TRANSACTION_DEAL_ADD)
     {
      long     deal_entry        =0;
      double   deal_volume       =0;
      string   deal_symbol       ="";
      long     deal_type         =0;
      long     deal_magic        =0;
      long     deal_positions_id =0;
      string   deal_comment      ="";
      if(HistoryDealSelect(trans.deal))
        {
         deal_entry=HistoryDealGetInteger(trans.deal,DEAL_ENTRY);
         deal_volume=HistoryDealGetDouble(trans.deal,DEAL_VOLUME);
         deal_symbol=HistoryDealGetString(trans.deal,DEAL_SYMBOL);
         deal_type=HistoryDealGetInteger(trans.deal,DEAL_TYPE);
         deal_magic=HistoryDealGetInteger(trans.deal,DEAL_MAGIC);
         deal_positions_id=HistoryDealGetInteger(trans.deal,DEAL_POSITION_ID);
         deal_comment=HistoryDealGetString(trans.deal,DEAL_COMMENT);
         //if(deal_magic==0 && SignalInfoGetString(SIGNAL_INFO_NAME)!=HistoryDealGetString(trans.deal,DEAL_COMMENT))
         //   return;
        }
      else
         return;
...

下表显示拷贝机在对冲和净持仓账户上的操作逻辑:

标记  意为此记录已作为结构的元素

标记 意为拷贝机不修改任何结构的元素

客户端 POSITION_IDENTIFIER 终端 POSITION_VOLUME 终端 POSITION_IDENTIFIER 拷贝机 deal_ticket
对冲。净持仓。 DEAL_ENTRY_IN
针对 trans.deal 值搜索 deal_ticket 结构元素。
如果没发现匹配项: 以增量 1 增加结构 ... 0 0 0 0
... 并认为它是服务成交 — 所以, 开一仓 (DEAL_VOLUME * coefficient), 且如果 CTrade.ResultDeal() != 0, 将 DEAL_POSITION_ID 的数值写入 POSITION_IDENTIFIER_terminal 元素, CTrade.ResultDeal() 的数值被写入 deal_ticket 元素, 而 deal_volume — 写入 POSITION_VOLUME_terminal 0
如果发现 – 则它是由拷贝机开仓的成交, 所以将 DEAL_POSITOIN_ID 写入 POSITION_IDENTIFIER_copier 元素。

对冲。净持仓。 DEAL_ENTRY_OUT
在 POSITION_IDENTIFIER_terminal 元素和 POSITION_IDENTIFIER_copier 结构里搜索 DEAL_POSITION_ID。 
对冲。
... 发现 DEAL_POSITION_ID...
... ... 在元素 POSITION_IDENTIFIER_copier – 不作任何事并离开  ✔  ✔  ✔  ✔
... ... 在元素 POSITION_IDENTIFIER_terminal –...  ✔  ✔  ✔  ✔
... ... ... 此仓位依然存在?如果它存在, 由拷贝机平仓...  ✔  ✔  ✔  ✔
... ... ... ... 开仓 (发现的仓位成交量 * 系数) 且如果 CTrade.ResultDeal() != 0, 将 CTrade.ResultDeal() 的数值写入 deal_ticket 元素。  ✔  ✔  ✔  ✔
... ... ... 无, 此仓位不再存在。由拷贝机平仓。  ✔  ✔  ✔  ✔
净持仓。
... 发现 DEAL_POSITION_ID...
... ... 在元素 POSITION_IDENTIFIER_terminal – (之前由信号服务开仓的成交量现在保存在结构里) 计算新的交易量, 且如果 CTrade.ResultDeal() != 0, 则将 CTrade.ResultDeal() 的数值写入 deal_ticket 元素。  ✔  ✔  ✔  ✔

结论

如果您订阅了一个信号, 租用了一台内建的虚拟主机, 但提供者仅以最小 (或极小) 手数交易 — 您可以将本文研究的拷贝机发送到内建的虚拟主机上。这样, 所有成交将依照拷贝机设置按比例增加。

注意: 拷贝机的 "copier.mq5" 文件和 "languages.mqh" 包含文件必须放置在相同的文件夹。


本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/2438

附加的文件 |
copier.mq5 (37.81 KB)
languages.mqh (6.19 KB)
图形界面 VII: 页面控件 (第二章) 图形界面 VII: 页面控件 (第二章)
第七部分的第一章介绍了用于创建三种表格控件的类: 文字标签型表格(CLabelsTable), 编辑框型表格(CTable) 以及绘制型表格(CCanvasTable)。在本文中(第二章)我们将讨论页面(Tabs)控件。
研究 CCanvas 类。抗锯齿和阴影 研究 CCanvas 类。抗锯齿和阴影
CCanvas 类的抗锯齿算法是所有使用抗锯齿构造的基础。本文包括其算法的操作内容, 并提供相应的直观示例。它也涵盖了绘制图形对象造型, 以及在画布上绘制造型的算法详情。数值分析库 ALGLIB 用于计算。
跨平台智能交易程序: 概论 跨平台智能交易程序: 概论
本文详细介绍了一种可以更快捷开发跨平台 EA 的方法。其所倡导的方法是将两个版本共享的功能整合到一个单独的类, 并将不兼容的功能分割到派生类。
图形界面 VII: 表格控件 (第一章) 图形界面 VII: 表格控件 (第一章)
MetaTrader 图形界面系列的第七部分处理的是三种表格类型:文本标签型,编辑框型,以及绘制型。另一种重要并且常用的控件是页面,它使您可以显示/隐藏成组的其他控件并且在您的MQL应用程序中开发有效利用空间的界面。