内容目录
- 风险警示
- 1. 准备
- 1.1. 拷贝机的思路
- 2. 信号服务的功能
- 2.1. 友方或是敌方
- 2.2. 是否取代魔幻数字?
- 2.3. 如果提供者将其平仓, "敌方" 仓位将会发生什么?
- 3. 如何检测成交出现
- 3.1. 使用 OnTradeTransaction() 跟踪
- 4. 何时可以订阅以及何时不可
- 5. 保存关于拷贝的信息
- 结论
风险警示
使用此方法之前, 您要依据以往的常识, 因为增加拷贝比率的同时意味着风险增加。
当跟单时, 系统周期性检查订阅者账户内仓位是否与提供者的匹配。如果检测到不匹配 (如, 只有部分仓位被拷贝), 则系统尝试消除并复制缺失的仓位。不像初始同步, 不检查提供者的初始总浮盈。如果订阅者已经开始跟单, 他们将会跟随提供者的交易策略来最大可能的拓展。一些不可能拷贝的仓位, 将会被忽略。
如果系统检测到订阅者账户内存在并非基于信号的仓位, 它会提示将它们平仓, 或者根据 "无需确认同步仓位" 设置来自动将它们平仓。
只有当提供者用最小手数交易, 在设置中将本金负载设为 95% (本金最大利用率), 同时交易拷贝比率依旧很小时, 用此方法增加信号拷贝的比率才有意义。您可以在文章 信号计算器 的帮助下, 在您订阅喜欢的信号之前了解今后的拷贝比率。
本文中表述的拷贝交易信号助理程序可以从市场中免费下载:
- 用于 MetaTrader 5 的信号拷贝机
1. 准备
在您开始之前, 请参见教程视频:
- 选择交易信号
- 订阅信号
- 租用虚拟主机
- 迁移订阅和智能交易程序到虚拟主机
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. 拷贝成交的备注 ("历史" 栏)
此处:
- "#69801797" — 成交号;
- "Test3443431" — 此拷贝成交的信号源名称;
- "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; // 成交单号, 如果成交已执行。 };
结构只包含四个元素:
- "POSITION_IDENTIFIER_terminal" — 此结构保存由终端 (信号服务) 打开的仓位标识符
- "POSITION_VOLUME_terminal" — 保存由终端 (信号服务) 打开的仓位成交量
- "POSITION_IDENTIFIER_copier" — 此元素保存由拷贝机打开的仓位标识符
- "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" 包含文件必须放置在相同的文件夹。