
根据品种和 EA 的 ORDER_MAGIC 分析余额/净值图形
内容
设置任务
随着对冲的引入, MetaTrader 5 提供了一个极佳的机会, 可以在一个交易账户内同时利用若干个专家交易系统 (或是策略) 进行交易。整个交易账户可以在 信号 服务中进行跟踪, 并具有接收统计数据的能力。然而, 仍然存在一个重要的问题: 如何直观各策略对余额和净值图形的贡献?
在交易中, 一个策略有盈利, 而第二个正在亏损的情形并不罕见。结果就是, 总体结果也许会徘徊在零值附近。在此情况下, 分别为每个交易策略构建余额和净值图形是十分有益的。
1. 佣金, 库存费, 利润
金融交易的总体结果通过汇总三个参数来定义:
结果=成交佣金 +收盘时的累计库存费+ 成交利润
这些交易属性可使用 HistoryDealGetDouble() 函数并用以下 ID 为参数得到:
DEAL_COMMISSION |
交易佣金 |
double |
DEAL_SWAP |
收盘时的累计库存费 |
double |
DEAL_PROFIT |
成交利润 |
double |
HistoryDealGetTicket.mq5 脚本, 是在指定时间段内从交易历史中接收交易属性的示例。
脚本的操作结果 (排除交易的 DEAL_ENTRY_IN 类型, 因为它们没有财务结果):
... 4: deal #36774600 at 2017.02.15 10:17:50 Entry out, sell vol: 0.01 comm: 0 swap: 0.02 profit: 1.52 NZDUSD.m (order #47802989, position ID 47770449) ... 12: deal #36798157 at 2017.02.15 16:44:17 Entry out, buy vol: 0.01 comm: 0 swap: -0.01 profit: 2.98 EURUSD.m (order #47827771, position ID 47685190) 13: deal #36798161 at 2017.02.15 16:44:17 Entry out, buy vol: 0.01 comm: 0 swap: -0.02 profit: 5.99 EURUSD.m (order #47827785, position ID 47665575) 14: deal #36798176 at 2017.02.15 16:44:17 Entry out, buy vol: 0.01 comm: 0 swap: -0.02 profit: 5.93 EURUSD.m (order #47827805, position ID 47605603) 15: deal #36798185 at 2017.02.15 16:44:18 Entry out, buy vol: 0.01 comm: 0 swap: -0.03 profit: 5.98 EURUSD.m (order #47827821, position ID 47502789) 16: deal #36798196 at 2017.02.15 16:44:18 Entry out, buy vol: 0.01 comm: 0 swap: -0.03 profit: 8.96 EURUSD.m (order #47827832, position ID 47419515) 17: deal #36798203 at 2017.02.15 16:44:18 Entry out, buy vol: 0.01 comm: 0 swap: -0.06 profit: 8.92 EURUSD.m (order #47827835, position ID 47130461) 18: deal #36798212 at 2017.02.15 16:44:19 Entry out, sell vol: 0.01 comm: 0 swap: -0.48 profit: -21.07 EURUSD.m (order #47827845, position ID 46868574) ... 25: deal #36824799 at 2017.02.15 19:57:57 Entry out, sell vol: 0.01 comm: 0 swap: 0 profit: 2.96 NZDUSD.m (order #47855548, position ID 47817757) 26: deal #36824800 at 2017.02.15 19:57:58 Entry out, sell vol: 0.01 comm: 0 swap: 0 profit: 3.01 NZDUSD.m (order #47855549, position ID 47790966) 27: deal #36824801 at 2017.02.15 19:57:58 Entry out, sell vol: 0.01 comm: 0 swap: 0.02 profit: 3.07 NZDUSD.m (order #47855550, position ID 47777495) 28: deal #36824802 at 2017.02.15 19:57:58 Entry out, sell vol: 0.01 comm: 0 swap: 0.02 profit: 3 NZDUSD.m (order #47855551, position ID 47759307) 29: deal #36824803 at 2017.02.15 19:57:59 Entry out, sell vol: 0.01 comm: 0 swap: 0.02 profit: 1.52 NZDUSD.m (order #47855552, position ID 47682775) ... 33: deal #36832775 at 2017.02.16 00:58:41 Entry out, sell vol: 0.01 comm: 0 swap: 0.05 profit: 2.96 NZDUSD.m (order #47863883, position ID 47826616) 34: deal #36832776 at 2017.02.16 00:58:41 Entry out, sell vol: 0.01 comm: 0 swap: 0.05 profit: 3.05 NZDUSD.m (order #47863884, position ID 47803010) 35: deal #36832777 at 2017.02.16 00:58:41 Entry out, sell vol: 0.01 comm: 0 swap: 0.05 profit: 2.98 NZDUSD.m (order #47863885, position ID 47792294) 36: deal #36832778 at 2017.02.16 00:58:42 Entry out, sell vol: 0.01 comm: 0 swap: 0.07 profit: 2.88 NZDUSD.m (order #47863886, position ID 47713741) ...
正如我们所见, 库存费和利润既有 "+" 亦有 "-" 符号。因此, 在总体财务结果方程中库存费和利润使用加法。
2. 计算历史余额和净值
通常操作原则包括形成 "持仓" 的清单。交易历史被划分为每 5-15 分钟一段 (此参数将在稍后定义)。那么我们应该先依次对每个一段执行以下操作:
- 搜索离场交易。如果检测到, 我们应该根据这些交易的总体财务结果重新计算余额和净值图形的数值。修正 "持仓" 清单
- 按照持仓计算净值图形数值。
我们需要成交类来考虑所选段落的持仓。我们会在 CHistoryDeal 类中实现它 — 在 HistoryDeal.mqh 包含文件中:
方法 | 数值 |
---|---|
TicketDeal | 取值/赋值 "Deal ticket" 属性 |
PosIDDeal | 取值/赋值 "Position ID" 属性 |
SymbolDeal | 取值/赋值 "Deal symbol" 属性 |
TypeDeal | 取值/赋值 "Deal type" 属性, 数值取自 ENUM_DEAL_TYPE 枚举 |
EntryDeal | 取值/赋值 "Trade direction" 属性, 数值取自 ENUM_DEAL_ENTRY 枚举 |
VolumeDeal | 取值/赋值 "Trade volume" 属性 |
PriceDeal | 取值/赋值 "Trade open price" 属性 |
盈利方程取自 ENUM_SYMBOL_CALC_MODE 枚举描述。在这样做时, 请始终记住计算品种利润的货币。可以获得利润货币:
- 通过 CSymbolInfo::CurrencyProfit 交易类
- 通过访问 SymbolInfoString 品种属性:
SymbolInfoString(m_name,SYMBOL_CURRENCY_BASE);
- 或是通过简单地打开终端中的品种规范:
图例. 1. RTS-3.17 规范
可能的难题:
- 在 "市场观察" 中缺失交易历史中的品种 (通过常用品种列表执行搜索);
- 品种的利润货币不能重新计算转换到存款货币。换言之, 在 "市场观察" 中没有用于重新计算的品种。
考虑到在程序的 (而非终端的!) 全局变量 中可能的错误, 我们引入一个数组, 用来保存 "市场观察" 中不存在的 "损坏" 品种, 以及无法将利润货币重新计算转换为存款利率的那些:
string m_arr_defective_symbols[]; // 有缺陷品种的数组
顺序通过检查 (所有阶段都通过 SearchDefectiveSymbols 函数):
- 为 "市场观察" 中存在的所有品种创建临时 (辅助) 数组;
- 为所有交易历史中的品种创建临时 (辅助) 数组 (在指定的日期段里: 从 ... 至 ... );
- 在 "市场观察" 中寻找交易历史中的品种。如果找不到任何品种, 则将其发送到 "损坏" 品种列表;
- 在 "市场观察" 中添加 (显示) 来自交易历史的品种。如果操作失败, 则将该品种发送到 "损坏" 品种列表;
- 从品种属性获取利润货币。如果品种的利润货币与存款货币不一致, 请转至 5.1 子步骤。
- 尝试找到一个 "市场观察" 品种重新计算。品种应符合 "存款货币" +"利润货币" 或 "利润货币" + "存款货币"。如果操作失败, 则将品种发送到 "损坏" 品种列表
"SearchDefectiveSymbols" 函数完成后, 来自交易历史的 "损坏" 品种数组业已形成。"损坏" 品种不会在未来的计算中使用 (余额和净值图形)。
所有余额变化均显示在 m_arr_balance_equity 二维数组中。
请求在 "从日期" 和 "至日起" 日期 (EA 输入) 之间的历史数据, 并以 "计时器 (分钟)" 期间的周期数将其切分。此操作是在 "CreateBalanceEquity" 函数中完成的。所有交易均被写入 "ArrOpenPositions" 动态指针数组中。随后的数组操作是添加和删除交易。
根据 "交易方向" 参数 — ENUM_DEAL_ENTRY, 余额和净值图表的计算方式不同。如果您想查看对冲和净持账户中开仓、平仓、部分平仓或反转持仓的行为, 您可以启动 HistorySelect.mq5 脚本。
- DEAL_ENTRY_IN — 入场。将一笔交易写入 ArrOpenPositions 数组
- DEAL_ENTRY_OUT — 离场。在 ArrOpenPositions 数组中搜索一笔 DEAL_ENTRY_IN 类型, 且 DEAL_POSITION_ID 相同的交易
- 如果搜索返回 "false", 则在余额和净值图形中不进行任何更改
- 如果 'true', 验证交易量 (以手数)
- 如果交易量相同, 从 ArrOpenPositions 数组里删除一笔交易, 并在余额图形数组 (m_arr_balance_equity) 里进行修改
- 如果来自 ArrOpenPositions 数组里的交易量较高, 调整在 ArrOpenPositions 数组里的交易量, 并在 余额图形 (m_arr_balance_equity 数组) 里进行修改
- 如果来自 ArrOpenPositions 数组里的交易量较低, 从 ArrOpenPositions 数组里删除一笔交易, 并在 余额图形 (m_arr_balance_equity 数组) 里进行修改
- DEAL_ENTRY_INOUT — 反转。在 ArrOpenPositions 数组搜索 DEAL_ENTRY_IN 类型且 DEAL_POSITION_ID 相同的一笔交易
- 如果搜索返回 "false", 则在余额和净值图形中不进行任何更改
- 如果 'true', 在 ArrOpenPositions 数组里调整交易量和类型, 并在余额图形 (m_arr_balance_equity 数组) 里进行修改
- DEAL_ENTRY_OUT_BY — 由相反仓位平仓。在 ArrOpenPositions 数组里搜索 DEAL_ENTRY_IN 类型且 DEAL_POSITION_ID 相同的一笔交易
- 如果交易量相同, 从 ArrOpenPositions 数组里删除一笔交易, 并在余额图形 (m_arr_balance_equity 数组) 里进行修改
- 如果交易量不同, 在 ArrOpenPositions 数组里调整交易量, 并在余额图形 (m_arr_balance_equity 数组) 里进行修改
有必要沿着整个 ArrOpenPositions 数组计算数组中所有交易的浮动利润。如果一笔交易已在 ArrOpenPositions 数组, 每笔交易我们都有一些如下的最重要数据:
- 品种
- 类型 (买入或卖出)
- 交易量
- 成交价格,
以及所请求的交易历史的最终日期。直到所请求的交易历史, 唯一缺少的数据是该笔交易最终日期的品种价格。价格在 CalculationFloatingProfit 里使用 CopyTicks 进行接收。如上所述, 盈利方程取自 ENUM_SYMBOL_CALC_MODE 枚举描述。
在分析时, CopyTicks 方法变得非常耗时, 尤其当 CalculationFloatingProfit 数组在一个品种上包含多笔交易时。在这种情况下, 由于品种和日期一样, 相同的数据被请求多次。我们已实现了 结果缓存 (CopyTicks) 来加速这一进程:
//--- 缓存结果 "CopyTicks" static string arr_name[]; static double arr_ask[]; static double arr_bid[]; static datetime prev_time=0; int number=-1; if(time>prev_time) { prev_time=time; ArrayFree(arr_name); ArrayFree(arr_ask); ArrayFree(arr_bid); } found=false; size=ArraySize(arr_name); if(size>0) for(int i=0;i<size;i++) { if(name==arr_name[i]) { number=i; found=true; break; } } if(found) { ArrayResize(ticks_array,1); ticks_array[0].ask=arr_ask[number]; ticks_array[0].bid=arr_bid[number]; } else { //--- int copy_ticks=-1; int count=0; while(copy_ticks==-1 && count<5) { copy_ticks=CopyTicks(name,ticks_array,COPY_TICKS_INFO,1000*(ulong)time,1); //if(copy_ticks==-1)
供给价和采购价结果将保存到本地数组: 如果在该时间段内已接收到品种的即时报价, 则这些即时报价将简单地从本地数组里获取。这种方法可将进程加速 10-15 倍。
3. 将 EA 集成到对话框 (面板)
现在, EA 的基础已经准备就绪了, 到了让界面更加友好的时候了。考虑到文章的名称 "根据品种和 EA 的 ORDER MAGIC 分析余额/净值图形", 有必要提供几种过滤功能:
- 显示所有/一个或几个选定的品种;
- 显示所有魔幻数字/由一个或几个选定的品种。
我们已经有 m_arr_all_trade_symbols 数组来参考所有交易品种。它是在全局程序层面上声明的。我们来引入另外一个数组 m_arr_all_magics 用来参考所有的魔幻数字。为此, 请升级 FillArrayTradeSymbols函数: 现在, m_arr_all_magics 数组也将会填充。
图例. 2. 面板的一般视图
在 "损坏" 品种、所有的交易品种和魔幻数字数组形成之后, 面板 (基于 CComboBox 类的元素) 上的这两个列表被填入: 左侧列表中填充所有的交易品种, 右侧的一个充满了所有的魔幻数字。在列表中, 首先选择所有的品种和魔幻数字:
图例. 3. 下拉列表
面板 EA 操作逻辑如下: 仅在按下 "开始" 按钮后, 系统检查两个下拉列表中的选择项。根据所选参数, 余额/净值图形根据交易历史记录绘制在列表中。
我决定将整个 EA 代码转移到面板类 (APHDialog.mqh), 这将花费太多时间。替代解决方案: 在面板类中引入 m_ready, m_symbol 和 m_magic 内部变量:
//+------------------------------------------------------------------+ //| 类 CAPHDialog | //| 用法: 控件应用的主要对话框 | //+------------------------------------------------------------------+ class CAPHDialog : public CAppDialog { private: CLabel m_label_symbols; // 标签对象 CComboBox m_combo_box_symbols; // 组合框对象 CLabel m_label_magics; // 标签对象 CComboBox m_combo_box_magics; // 组合框对象 CButton m_button_start; // 按钮对象 //--- bool m_ready; // true -> 您可以构建图形 string m_symbol; ulong m_magic; public:
当处理 开始 按钮时, 会在 m_ready 变量状态基础上进行决策:
//+------------------------------------------------------------------+ //| 事件处理器 | //+------------------------------------------------------------------+ void CAPHDialog::OnClickButtonStart(void) { if(m_combo_box_symbols.Select()!=" " && m_combo_box_magics.Select()!=" ") m_ready=true; else m_ready=false; //Comment(__FUNCTION__+" ButtonStartclick"+"\n"+ // "品种: "+"\""+m_combo_box_symbols.Select()+"\""+"\n"+ // "魔幻数字: "+"\""+m_combo_box_magics.Select()+"\""+"\n"+ // "m_ready: "+IntegerToString(m_ready)); }
注意, 高亮字符串: 一旦创建并填充下拉列表 (在我们的案例中这些是 m_combo_box_symbols and m_combo_box_magics 元素) 之后, 带有 " " 值 (空格) 的元素设置在列表里。
在处理相应下拉列表上的点击时, 会在 m_symbol 和 m_magic 变量状态基础上做出决策:
//+------------------------------------------------------------------+ //| 事件处理器 | //+------------------------------------------------------------------+ void CAPHDialog::OnChangeComboBoxSymbols(void) { //Comment(__FUNCTION__+" \""+m_combo_box_symbols.Select()+"\""); if(m_combo_box_symbols.Select()=="所有品种") m_symbol=""; else m_symbol=m_combo_box_symbols.Select(); } //+------------------------------------------------------------------+ //| 事件处理器 | //+------------------------------------------------------------------+ void CAPHDialog::OnChangeComboBoxMagics(void) { //Comment(__FUNCTION__+" \""+m_combo_box_magics.Select()+"\""); if(m_combo_box_magics.Select()=="所有魔幻数字") m_magic=-1; else m_magic=StringToInteger(m_combo_box_magics.Select()); }
所以, 当单击开始按钮时, 将填充三个变量 m_ready, m_symbol 和 m_magic。现在, 所有要做的就是通知 EA 已在面板中选择了参数。解决方案很简单: 在 EA 中, 我们应该以三秒钟的间隔启动定时器。定时对面板进行调查。为此, 我们在面板中写下 CAPHDialog::IsReady 方法。
//+------------------------------------------------------------------+ //| 在面板上有所选择的参数 | //+------------------------------------------------------------------+ bool CAPHDialog::IsReady(string &symbol,ulong &magic) { if(m_ready) { symbol=m_symbol; magic=m_magic; m_ready=false; return(true); } else return(false); }
在此方法中, 我们将内部变量的值写入通过引用传递的变量里, 并重置 m_ready 内部变量。
根据指定条件选择交易: 在 GetHistory 里根据品种, 或是所有品种和魔幻数字, 或是所有魔幻数字:
//--- 所有成交 for(int i=0;i<deals;i++) { deal_ticket = HistoryDealGetTicket(i); deal_position_ID = HistoryDealGetInteger(deal_ticket,DEAL_POSITION_ID); deal_symbol = HistoryDealGetString(deal_ticket,DEAL_SYMBOL); deal_type = (ENUM_DEAL_TYPE)HistoryDealGetInteger(deal_ticket,DEAL_TYPE); deal_entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal_ticket,DEAL_ENTRY); deal_volume = HistoryDealGetDouble(deal_ticket,DEAL_VOLUME); deal_price = HistoryDealGetDouble(deal_ticket,DEAL_PRICE); deal_commission = HistoryDealGetDouble(deal_ticket,DEAL_COMMISSION); deal_swap = HistoryDealGetDouble(deal_ticket,DEAL_SWAP); deal_profit = HistoryDealGetDouble(deal_ticket,DEAL_PROFIT); deal_magic = HistoryDealGetInteger(deal_ticket,DEAL_MAGIC); if(sSymbol!="") if(deal_symbol!=sSymbol) continue; if(uMagic!=ULONG_MAX) if(deal_magic!=uMagic) continue; //--- 仅买入或卖出
请注意, 对于 uMagic 变量, ULONG_MAX 意味着 "所有魔幻数字", 而对于 sSymbol 变量, "" 意味着 "所有品种"。
根据交易历史绘制余额和浮动利润 (首先, 对于所有品种, 然后 — 仅一个):
视频
4. MFE 和 MAE 分布图形
最大盈利 (MFE) 和最大亏损 (MAE) 值是在每笔开单的生存期之间录得的。每笔平仓订单的附加表征参数, 最大未实现潜力和最大允许风险值。MFE/盈利 和 MAE/盈利 分布图将每笔订单显示为沿 X 轴绘制的盈利/亏损值的点数, 而最大潜在利润 (MFE) 和潜在亏损(MAE) 则沿 Y 轴绘制。
用于仓位记账的 m_arr_pos_id 数组, 以及参考 MFE 的 m_arr_mfe_mae数组, 总体财务结果和 MAE 在全局程序层面上声明:
long m_arr_pos_id[]; double m_arr_mfe_mae[][3]; // [][0] - mfe, [][1] - 最终财务结果,[][2] - mae
随后, 基于 m_arr_mfe_mae 数组绘制 MFE 和 MAE 分布点数图形。
用于仓位记账的 m_arr_pos_id 数组, 以及参考 MFE 的 m_arr_mfe_mae 在第一维里总有一个大小 — 所以, "i" 位置 (m_arr_pos_id[i]) 总是清晰的匹配 m_arr_mfe_mae[i][][][]。这两个数组的大小设置在 GetHistory 中:
if(deal_symbol=="") DebugBreak(); ArrOpenPositions.Add(HistoryDeal); //--- mfe, mae int size=ArraySize(m_arr_pos_id); ArrayResize(m_arr_pos_id,size+1,10); ArrayResize(m_arr_mfe_mae,size+1,10); m_arr_pos_id[size]=deal_position_ID; // [][0] - mfe, [][1] - 最终财务结果,[][2] - mae m_arr_mfe_mae[size][0]=0.0; m_arr_mfe_mae[size][1]=0.0; m_arr_mfe_mae[size][2]=0.0; continue; } //--- if(deal_entry==DEAL_ENTRY_OUT)
函数负责记录每笔仓位的最大盈利和亏损:
//+------------------------------------------------------------------+ //| 加入 Mfe Mae 结果 | //+------------------------------------------------------------------+ void AddResultMfeMae(const long pos_id,const double floating_profit,const double financial_result) { // [][0] - mfe (盈利), [][1] - 最终财务结果,[][2] - mae (亏损) //--- 搜索 pos_id int position=-1; int size=ArraySize(m_arr_pos_id); for(int i=0;i<size;i++) if(m_arr_pos_id[i]==pos_id) { position=i; break; } if(position==-1) return; //--- if(floating_profit==0.0) return; if(floating_profit>0.0) // profit { if(m_arr_mfe_mae[position][0]<floating_profit) m_arr_mfe_mae[position][0]=floating_profit; } else // loss { if(m_arr_mfe_mae[position][2]>floating_profit) m_arr_mfe_mae[position][2]=floating_profit; } m_arr_mfe_mae[position][1]=financial_result; }
所有三个参数都应传递。换言之, 如果我们的虚拟仓位仍然位于 ArrOpenPositions 持仓列表, 且其浮盈为 -20.2, 则看起来如下:
AddResultMfeMae(pos_id,-20.2,0.0);
如果我们的虚拟仓位仍然位于 ArrOpenPositions 持仓列表, 且其浮盈为 +5.81, 则看起来如下:
AddResultMfeMae(pos_id,5.81,0.0);
如果我们的虚拟仓位仍然位于 ArrOpenPositions 持仓列表, 且其浮盈为 -3.06, 则看起来如下:
AddResultMfeMae(pos_id,0.0,-3.06);
换言之, 如果有浮盈, 总体财务结果为 0。如果仓位已平仓, 浮动利润等于 0。
- DEAL_ENTRY_IN - 入场。写入一笔交易至 ArrOpenPositions 数组。我们在 m_arr_pos_id 和 m_arr_mfe_mae 数组里创建一个新元素。
- DEAL_ENTRY_OUT - 离场。在 ArrOpenPositions 数组里搜索一笔 DEAL_ENTRY_IN 类型的交易, 且 DEAL_POSITION_ID 相同
- 如果搜索返回 "false", 则在余额和净值图形中不进行任何更改
- 如果 'true', 验证交易量 (以手数)
- 如果交易量相同, 从 ArrOpenPositions 数组里删除一笔交易, 并修改余额图形 (m_arr_balance_equity)。 写入 [浮盈 0.0][总体财务结果] 至 m_arr_mfe_mae 数组。
- 如果来自 ArrOpenPositions 数组的交易量较高, 在 ArrOpenPositions 数组里调整交易量, 并修改 m_arr_balance_equity 数组 (余额图形)。 写入 [浮盈 0.0][总体财务结果] 至 m_arr_mfe_mae 数组。
- 如果来自 ArrOpenPositions 数组的交易量较低, 从 ArrOpenPositions 数组里删除一笔交易, 并修改 m_arr_balance_equity 数组 (余额图形)。 写入 [浮盈 0.0][总体财务结果] 至 m_arr_mfe_mae 数组。
- DEAL_ENTRY_INOUT - 反转。在 ArrOpenPositions 数组里搜索一笔 DEAL_ENTRY_IN 类型的交易, 且 DEAL_POSITION_ID 相同
- 如果搜索返回 "false", 则在余额和净值图形中不进行任何更改
- 若为 'true', 在 ArrOpenPositions 数组里调整交易量和类型 , 并修改 m_arr_balance_equity 数组 (余额图表)。 写入 [浮盈 0.0][总体财务结果] 至 m_arr_mfe_mae 数组。
- DEAL_ENTRY_OUT_BY - 由相反仓位平仓。在 ArrOpenPositions 数组里搜索一笔 DEAL_ENTRY_IN 类型的交易, 且 DEAL_POSITION_ID 相同
- 如果交易量相同, 从 ArrOpenPositions 数组里删除一笔交易, 并修改余额图形 (m_arr_balance_equity)。 写入 [浮盈 0.0][总体财务结果] 至 m_arr_mfe_mae 数组。
- 如果交易量不同, 在 ArrOpenPositions 数组里调整交易量, 并修改 m_arr_balance_equity 数组 (余额图形)。 写入 [浮盈 0.0][总体财务结果] 至 m_arr_mfe_mae 数组。
此外, 如果您打算重新计算每笔仓位的浮盈, 在 GetHistory 里, 写入 [浮盈][总体财务结果 0.0] 至 m_arr_mfe_mae 数组。
为了显示余额/净值, 和 MFE/MAE 图形, 对话框应稍作修改:
图例. 4. 修改对话框
MFE (最大盈利) 和 MAE (最大亏损) 使用两个坐标构建: 分别对应 X - 仓位的总体财务结果, Y - MFE 或 MAE 数值。
图例. 5. MFE
图例. 6. MAE
结论
现在, 即使在对冲账户里同时使用几个 EA, 您也可以查看每个品种和魔幻数字的余额和净值图形。换言之, 您可以直观地定义每个 EA (ORDER_MAGIC) 对总体余额的贡献, 以及每个 EA 的回撤。
文章中使用的程序:
# |
名称 |
类型 |
描述 |
---|---|---|---|
1 |
HistoryDeal.mqh | 函数库 | 在所选段落上分析持仓的类 |
2 |
HistoryDealGetTicket.mq5 | EA | 在指定时间段内从交易历史中接收交易属性的示例 |
3 |
APHDialog.mqh | 函数库 | EA 对话框类 |
4 |
Accounting_positions_on_history.mq5 | EA | 文章中描述的主要 EA |
5 |
MQL5.zip | 存档 | 包含主要 EA 及其包含文件的存档。 |
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/3046
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.



感谢您的关注。问题是,我之前没有遇到过佣金问题,所以使用了 ServiceDesk 的提示。同时,我也没有检查可以收取佣金的真实账户。
代码和文本将得到更正。
我不明白为什么计算速度这么慢?起初我以为是使用了特殊的减速方法,但我在资料中没有找到 "Sleep "和 "OnTimer"。
计算资源 的一种破坏。这就是为什么要尽量减少调用次数的原因。尤其是 HistorySelectByPosition。
"MetaTrader 5 引入对冲功能后,在一个交易账户上同时交易多个智能交易系统(Expert Advisors)的机会大大增加。在这种情况下,有可能出现一种策略盈利,第二种策略不盈利,最后盈利图为零的情况。在这种情况下,为每个交易策略分别建立余额和资金图表是非常有用的"。
这个话题确实非常重要,提出的问题也很严重--MT5 不具备在同一账户和符号(通过MAGICs 区分)上运行的交易策略的 PnL 计算功能,但是......作者只是在胡说八道!显然,弗拉基米尔只是一名程序员,而不是交易员,因此根本不了解这个主题。
主要文章中不正确的佣金符号只是小事一桩。这里的问题是,这篇文章根本没有解决 交易策略 中 缺乏PnL 计算的既定问题,非但 没有解决问题,反而展示了作者通过 OOP 和编程面板选择交易的技巧。
真正的问题在于,MT5在计算任何地方的 PnL 值时,都 没有将MAGIC 考虑在内 ,因此 交易的 DEAL_PROFIT 计算一般都不正确。例如,如果我有三个智能交易系统在一个符号上进行交易:一个趋势捕捉器在长期内建仓,两个震荡器在H1/H4 上捕捉超买/超卖时段,并返回平均值进行交易,那么在不考虑MAGIC 的 情况下计算出的交易 PnL只是 "医院的温度"。在 20 个不同的符号上有三个 EA 的情况下呢?
因此, 对于平仓的 所有交易 ,GetHistory 函数中的求和 (_balance=m_balance+deal_commission+deal_swap+deal_profit;)是不正确的。DEAL_PROFIT 必须在求和之前重新计算。重新计算时要考虑到平仓日的汇率,例如在以美元为基准货币的交易账户中进行 MOEX 期货交易时。
非常有用。弗拉基米尔,我注意到一个错误--账户是 4 天前开设的。
账户是 4 天前开设的。我们在智能交易系统中选择了默认开始日期,即 2017 年。
我们选择的结束日期是 2018 年 10 月 2 日。
智能交易系统挂起,不响应以十字星收盘。同时,终端消耗了 2GB 内存。
我认为比较开户日期和用户在搜索前指定的搜索开始日期更为正确。
非常有用。弗拉基米尔,我注意到了这个错误--"......"。
账户是 4 天前开设的。我们在智能交易系统中选择了默认开始日期,即 2017 年。
结束日期是今天 2018 年 10 月 2 日。
智能交易系统挂起,不响应以十字星关闭。终端占用了 2GB 内存。
我认为比较开户日期和用户在搜索前指定的搜索开始日期更为正确。
请指明您正在运行的文件名称。交易账户类型。在哪个符号上。