
构建蜡烛图趋势约束模型(第8部分):EA开发(II)
内容:
- 引言
- 创建一个EA:
- (i) 在MetaEditor中加载默认EA(模版)。
- (ii) 自定义模版。
- (iii) 将趋势约束EA的逻辑编写到准备好的模板中。
- 测试器
- 结论
引言
在我们之前的文章中,我们探讨了使用Trend Constraint V1.09指标创建EA,并辅以手动执行的Trend Constraint R-R脚本用于绘制风险和收益矩形。虽然这种设置提供了富有洞察力的交易信号并增强了可视化效果,但它需要手动干预,而这一过程是可以简化的。鉴于交易环境的快节奏特性,对更高效解决方案的需求变得显而易见。许多交易者寻求能够自主运行的集成系统,以减少持续监督和手动执行的需要。
本文是本系列的下一步,将指导您开发一个独立的EA,它不仅整合了Trend Constraint V1.09的趋势分析能力,还将风险收益功能直接集成到EA中。我们的目标是利用MetaTrader 5平台上的MQL5为交易者提供一个一体化解决方案,通过增强自动化和无缝操作来跟上市场的需求。
为了实现这一目标,我们将回顾在第一部份,第一部份,和第三部份中涵盖的基础阶段,并借鉴EA执行交易任务所需的逻辑。现在,我们将结合所有这些部分及其逻辑来编写EA。
以下是指标在MQL5中实现信号生成条件的总结:
买入(多头)条件:
卖出(空头)条件:
- 日K线必须是看涨的。
- 在一个看涨的日K线图内的较低时间框架上,我们需要信号的汇聚。具体来说,内置指标(如相对强弱指数RSI)应显示超卖状态,且快速移动平均线必须向上穿过慢速移动平均线。
- 日K线图必须是看跌的。
- 在一个看跌的日K线图内的较低时间框架上,我们需要信号的汇聚。具体来说,内置指标(如相对强弱指数RSI)应显示超买状态,且快速移动平均线必须向下穿过慢速移动平均线。
创建一个EA
我们使用MetaEditor应用程序编写我们的MQL5 EA。最重要的是要牢记基本原则。以下是我们EA的总体结构。
大多数EA程序的架构总结:
- 初始化(OnInit):设置必要的指标和变量。
- 主循环(OnTick):处理传入的tick,以评估市场条件并做出交易决策。
- 交易管理(OnTrade):处理与交易相关的事件。
- 测试(OnTester及相关函数):为在策略测试器中优化和评估EA的性能提供结构。
- 用户交互(OnChartEvent):可选,但允许通过图表事件与EA进行交互。
我相信下面的流程图草图将有助于澄清我们期望的结果,使其更容易理解。
EA各部分及其相互关联的简略流程图
这种结构确保了我们的EA组织良好,并能够有效地处理实施交易策略所需的各种任务。我将开发过程分为几个子部分,但高级开发人员可以跳过阶段(i)和(ii),直接进入子部分(iii)。(ii) 自定义模版。
(iii) 将趋势约束EA的逻辑编写到准备好的模板中。
(i)启动EA模板
在MetaEditor中,按下Ctrl + N启动一个新文件,并选择“Expert Advisor (Template)”(EA模板),如下图所示:
启动一个新的EA模板
请注意,由于我之前在电脑上进行过项目开发,因此在启动新项目时,作者详情和链接已经自动填充。您可以根据自己的需求进行自定义。
(ii) 自定义模版
本文的写作目的不仅是为了展示我的编程能力,还希望对那些刚刚开始理解开发过程的初学者产生影响,以便他们能够在未来应用所学到的技能。在这一点上,我们正在查看模板,这可能对初学者来说似乎没有太多意义。开始在框架模板中编写代码之前,我将提取并简要解释所需最重要的函数。然后,我们将继续填充细节,编写符合我们需求的EA代码。这种方法至关重要,因为它将使您能够轻松地启动其他项目,这些项目与我们现在关注的内容不同。
框架模板为我们后续构建EA逻辑提供了基础。
以下是MetaEditor生成的模板代码:
//+------------------------------------------------------------------+ //| Trend Constraint Expert.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com/en/users/billionaire2024/seller | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/en/users/billionaire2024/seller" #property version "1.00" //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+ //| Trade function | //+------------------------------------------------------------------+ void OnTrade() { //--- } //+------------------------------------------------------------------+ //| Tester function | //+------------------------------------------------------------------+ double OnTester() { //--- double ret=0.0; //--- //--- return(ret); } //+------------------------------------------------------------------+ //| TesterInit function | //+------------------------------------------------------------------+ void OnTesterInit() { //--- } //+------------------------------------------------------------------+ //| TesterPass function | //+------------------------------------------------------------------+ void OnTesterPass() { //--- } //+------------------------------------------------------------------+ //| TesterDeinit function | //+------------------------------------------------------------------+ void OnTesterDeinit() { //--- } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- } //+------------------------------------------------------------------+
从模板中,让我们来讨论对我们EA来说至关重要的关键函数:
- OnInit() 函数:当EA初始化时,该函数仅运行一次,用于设置运行所需的指标、变量或其他资源。正确的初始化确保了在EA开始处理市场数据之前,所有必要的资源都已准备就绪。对于您的“趋势约束EA”,相对强弱指数(RSI)指标和其他关键变量通常会在这里初始化。
- OnDeinit() 函数:当EA被反初始化时,例如从图表中移除或关闭终端时,会调用此函数。它用于清理资源,例如释放指标句柄或关闭文件,以防止内存泄漏或其他问题。
- OnTick() 函数:这是主函数,每次EA所附加的交易品种接收到新的tick(价格更新)时都会触发。在您的“趋势约束EA”中,它将包含检查市场条件(如D1趋势和RSI水平)以及做出交易决策(如开仓或平仓)的逻辑。
- OnTrade() 函数:当发生交易事件时(例如下单、修改或平仓),此函数会被调用,它对于监控交易状态和对变化做出反应至关重要。例如,您可以使用它来跟踪交易何时开启,并相应地调整EA的行为。
- OnTester() 函数:在策略测试期间使用此函数返回一个双精度值,作为优化的自定义标准。它允许您定义一个自定义指标(如利润因子或回撤)来评估在策略测试器中测试EA的性能。
- OnTesterInit()、OnTesterPass() 和 OnTesterDeinit() 函数:这些函数专门用于策略测试和优化,管理策略测试器中的测试开始、正在进行的测试轮次以及测试结束。它们通过初始化资源、收集数据以及在测试后清理,为测试过程提供了更大的控制权。
- OnChartEvent() 函数:此函数用于处理图表事件,例如鼠标点击或按键操作,从而在EA运行时与之交互。如果您的EA包含用户交互功能(例如通过图表事件更改参数或触发操作),则此函数至关重要。
通过了解这些函数,我们可以看到如何在它们内部构建逻辑。然而,模板提供的函数可能不足以满足我们正在开发的EA的复杂性。可能需要额外的函数,并且我们需要证明并解释它们的加入,以满足我们“趋势约束EA”的特定需求。
未包含在EA模板中但对我们的项目必要的额外函数:- 指标函数(例如 iRSI):用于计算相对强弱指数(RSI)等对您的交易策略至关重要的指标。
- 交易函数(CTrade 类):用于管理订单和仓位,例如下达买入/卖出订单、设置止损以及修改仓位。
//+------------------------------------------------------------------+ //| Trend Constraint Expert Advisor.mq5| //| Copyright 2024, Clemence Benjamin| //| https://www.mql5.com/en/users/billionaire2024/seller| //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Initialization code here rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE); if (rsi_handle == INVALID_HANDLE) { Print("Failed to create RSI indicator handle"); return(INIT_FAILED); } // Any other initialization tasks return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Cleanup code here IndicatorRelease(rsi_handle); // Release RSI indicator handle // Any other deinitialization tasks } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Main trading logic goes here // Determine market conditions (e.g., daily trend, RSI levels) // Check for trade conditions and execute orders if necessary // Implement trailing stop logic if necessary } //+------------------------------------------------------------------+ //| Trade event function | //+------------------------------------------------------------------+ void OnTrade() { // Handle trade events (e.g., order placement, modification, closure) } //+------------------------------------------------------------------+ //| Tester function | //+------------------------------------------------------------------+ double OnTester() { double ret = 0.0; // Custom optimization criteria (if any) go here return(ret); } //+------------------------------------------------------------------+ //| TesterInit function | //+------------------------------------------------------------------+ void OnTesterInit() { // Initialization for testing (if needed) } //+------------------------------------------------------------------+ //| TesterPass function | //+------------------------------------------------------------------+ void OnTesterPass() { // Actions after each optimization pass (if needed) } //+------------------------------------------------------------------+ //| TesterDeinit function | //+------------------------------------------------------------------+ void OnTesterDeinit() { // Cleanup after testing (if needed) } //+------------------------------------------------------------------+ //| Chart event function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { // Handle chart events (e.g., mouse clicks, key presses) here } //+------------------------------------------------------------------+
(iii) 编写趋势约束EA逻辑。
现在,我们可以继续编写构成EA的每个函数的逻辑。让我们逐步构建我们的程序:
包含交易库:
我们首先包含交易库,这是必要的,因为该库中的CTrade类提供了执行交易操作(如开仓、修改仓位和平仓)所需的函数。通过包含这个库,我们使EA能够与市场交互并以编程方式管理交易。
#include <Trade\Trade.mqh> // Include the trade library定义输入参数:
接下来,我们定义输入参数,用户可以根据自己的交易偏好进行调整。这些参数包括RSI周期、超买和超卖水平、手数、止损、获利和跟踪止损。通过编写这些输入行,我们确保EA可以根据不同的市场条件进行定制。程序将使用这些值来决定何时进入和退出交易。
// Input parameters input int RSI_Period = 14; // RSI period input double RSI_Overbought = 70.0; // RSI overbought level input double RSI_Oversold = 30.0; // RSI oversold level input double Lots = 0.1; // Lot size input double StopLoss = 100; // Stop Loss in points input double TakeProfit = 200; // Take Profit in points input double TrailingStop = 50; // Trailing Stop in points声明全局变量:
然后,我们声明全局变量,例如RSI_value和RSI_handle,它们将分别存储RSI值和句柄,以及CTrade类的一个实例。通过声明这些变量,我们确保EA可以在不同函数之间保持状态,允许程序在其运行过程中根据需要访问和修改这些值。
// Global variables double rsi_value; int rsi_handle; CTrade trade; // Declare an instance of the CTrade class初始化EA:
在OnInit函数中,我们使用iRSI函数创建RSI指标句柄。这一步至关重要,因为程序需要这个句柄来在每个tick中获取RSI值。如果无法创建句柄,我们已经编写程序使其返回INIT_FAILED,以防止EA在缺少这个关键组件的情况下运行。这确保了程序只有在完全具备分析市场数据的能力时才会运行。
int OnInit() { // Create an RSI indicator handle rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE); if (rsi_handle == INVALID_HANDLE) { Print("Failed to create RSI indicator handle"); return(INIT_FAILED); } return(INIT_SUCCEEDED); }反初始化EA:
为了有效管理资源,我们实现了OnDeinit函数,在EA从图表中移除时释放RSI指标句柄。通过编写这段清理代码,我们防止了内存泄漏,并确保资源被正确释放。程序将在反初始化时自动执行这段清理代码,以保持最佳性能。
void OnDeinit(const int reason) { // Release the RSI indicator handle IndicatorRelease(rsi_handle); }实现核心交易逻辑:
核心交易逻辑位于OnTick函数中,它将在市场的每个tick到来时执行。首先,我们编写代码通过比较日K线的开盘价和收盘价来确定当前的日线趋势。这种分析使程序能够判断市场是看涨还是看跌,这对于做出明智的交易决策至关重要。
void OnTick() { // Determine current daily trend (bullish or bearish) double daily_open = iOpen(_Symbol, PERIOD_D1, 0); double daily_close = iClose(_Symbol, PERIOD_D1, 0); bool is_bullish = daily_close > daily_open; bool is_bearish = daily_close < daily_open;获取RSI值:
然后,我们使用CopyBuffer和之前创建的RSI句柄来获取RSI值。通过编写这段代码,我们确保程序能够判断市场是否处于超买或超卖状态。程序将在决策过程中使用这个RSI值,以确定是否满足进入交易的条件。
// Get the RSI value for the current bar double rsi_values[]; if (CopyBuffer(rsi_handle, 0, 0, 1, rsi_values) <= 0) { Print("Failed to get RSI value"); return; } rsi_value = rsi_values[0];在趋势变化时平仓:
我们还加入了逻辑,如果市场趋势发生变化,EA将关闭所有未平仓头寸。例如,如果趋势从看涨变为看跌,EA将关闭所有未平仓的多头头寸,反之亦然。通过编写这一保护机制,我们确保程序与当前的市场情绪保持一致,这对于最小化风险至关重要。
// Close open positions if the trend changes for (int i = PositionsTotal() - 1; i >= 0; i--) { if (PositionSelect(PositionGetSymbol(i))) // Corrected usage { int position_type = PositionGetInteger(POSITION_TYPE); ulong ticket = PositionGetInteger(POSITION_TICKET); // Get the position ticket if ((position_type == POSITION_TYPE_BUY && is_bearish) || (position_type == POSITION_TYPE_SELL && is_bullish)) { trade.PositionClose(ticket); // Use the ulong variable directly } } }检查买入和卖出条件:
对于买入和卖出条件,我们编写逻辑来检查看涨趋势与超卖RSI的组合用于买入订单,以及看跌趋势与超买RSI的组合用于做空订单。通过编程这些条件,我们确保EA只有在趋势和动量指标达成一致时才会进入交易。程序将监控这些条件并相应地执行交易,确保交易纪律。
// Check for buy condition (bullish trend + RSI oversold) if (is_bullish && rsi_value < RSI_Oversold) { // No open positions? Place a buy order if (PositionsTotal() == 0) { double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double sl = price - StopLoss * _Point; double tp = price + TakeProfit * _Point; // Open a buy order trade.Buy(Lots, _Symbol, price, sl, tp, "TrendConstraintExpert Buy"); } } // Check for sell condition (bearish trend + RSI overbought) if (is_bearish && rsi_value > RSI_Overbought) { // No open positions? Place a sell order if (PositionsTotal() == 0) { double price = SymbolInfoDouble(_Symbol, SYMBOL_BID); double sl = price + StopLoss * _Point; double tp = price - TakeProfit * _Point; // Open a sell order trade.Sell(Lots, _Symbol, price, sl, tp, "TrendConstraintExpert Sell"); } }实现一种追踪止损机制:
最后,我们实现了一个跟踪止损机制,以在市场朝着有利方向移动时保护利润。通过编写跟踪止损逻辑,我们确保EA能够动态调整止损,锁定利润,同时允许交易在市场保持有利的情况下继续进行。程序将自动管理跟踪止损,确保其对市场走势做出响应,以最大化收益并最小化损失。
// Apply trailing stop for (int i = PositionsTotal() - 1; i >= 0; i--) { if (PositionSelect(PositionGetSymbol(i))) // Corrected usage { double price = PositionGetDouble(POSITION_PRICE_OPEN); double stopLoss = PositionGetDouble(POSITION_SL); double current_price; if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID); if (current_price - price > TrailingStop * _Point) { if (stopLoss < current_price - TrailingStop * _Point) { trade.PositionModify(PositionGetInteger(POSITION_TICKET), current_price - TrailingStop * _Point, PositionGetDouble(POSITION_TP)); } } } else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { current_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); if (price - current_price > TrailingStop * _Point) { if (stopLoss > current_price + TrailingStop * _Point || stopLoss == 0) { trade.PositionModify(PositionGetInteger(POSITION_TICKET), current_price + TrailingStop * _Point, PositionGetDouble(POSITION_TP)); } } } } } }
我们最终的程序,包括标题和其他属性:
//+------------------------------------------------------------------+ //| Trend Constraint Expert.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com/en/users/billionaire2024/seller | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/en/users/billionaire2024/seller" #property version "1.00" #property description "A System that seeks to Long D1 Bullish sentiment and short D1 Bearish sentiment" #property strict #include <Trade\Trade.mqh> // Include the trade library // Input parameters input int RSI_Period = 14; // RSI period input double RSI_Overbought = 70.0; // RSI overbought level input double RSI_Oversold = 30.0; // RSI oversold level input double Lots = 0.1; // Lot size input double StopLoss = 100; // Stop Loss in points input double TakeProfit = 200; // Take Profit in points input double TrailingStop = 50; // Trailing Stop in points // Global variables double rsi_value; int rsi_handle; CTrade trade; // Declare an instance of the CTrade class //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Create an RSI indicator handle rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE); if (rsi_handle == INVALID_HANDLE) { Print("Failed to create RSI indicator handle"); return(INIT_FAILED); } return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Release the RSI indicator handle IndicatorRelease(rsi_handle); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Determine current daily trend (bullish or bearish) double daily_open = iOpen(_Symbol, PERIOD_D1, 0); double daily_close = iClose(_Symbol, PERIOD_D1, 0); bool is_bullish = daily_close > daily_open; bool is_bearish = daily_close < daily_open; // Get the RSI value for the current bar double rsi_values[]; if (CopyBuffer(rsi_handle, 0, 0, 1, rsi_values) <= 0) { Print("Failed to get RSI value"); return; } rsi_value = rsi_values[0]; // Close open positions if the trend changes for (int i = PositionsTotal() - 1; i >= 0; i--) { if (PositionSelect(PositionGetSymbol(i))) // Corrected usage { int position_type = PositionGetInteger(POSITION_TYPE); ulong ticket = PositionGetInteger(POSITION_TICKET); // Get the position ticket if ((position_type == POSITION_TYPE_BUY && is_bearish) || (position_type == POSITION_TYPE_SELL && is_bullish)) { trade.PositionClose(ticket); // Use the ulong variable directly } } } // Check for buy condition (bullish trend + RSI oversold) if (is_bullish && rsi_value < RSI_Oversold) { // No open positions? Place a buy order if (PositionsTotal() == 0) { double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double sl = price - StopLoss * _Point; double tp = price + TakeProfit * _Point; // Open a buy order trade.Buy(Lots, _Symbol, price, sl, tp, "TrendConstraintExpert Buy"); } } // Check for sell condition (bearish trend + RSI overbought) if (is_bearish && rsi_value > RSI_Overbought) { // No open positions? Place a sell order if (PositionsTotal() == 0) { double price = SymbolInfoDouble(_Symbol, SYMBOL_BID); double sl = price + StopLoss * _Point; double tp = price - TakeProfit * _Point; // Open a sell order trade.Sell(Lots, _Symbol, price, sl, tp, "TrendConstraintExpert Sell"); } } // Apply trailing stop for (int i = PositionsTotal() - 1; i >= 0; i--) { if (PositionSelect(PositionGetSymbol(i))) // Corrected usage { double price = PositionGetDouble(POSITION_PRICE_OPEN); double stopLoss = PositionGetDouble(POSITION_SL); double current_price; if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID); if (current_price - price > TrailingStop * _Point) { if (stopLoss < current_price - TrailingStop * _Point) { trade.PositionModify(PositionGetInteger(POSITION_TICKET), current_price - TrailingStop * _Point, PositionGetDouble(POSITION_TP)); } } } else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { current_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); if (price - current_price > TrailingStop * _Point) { if (stopLoss > current_price + TrailingStop * _Point || stopLoss == 0) { trade.PositionModify(PositionGetInteger(POSITION_TICKET), current_price + TrailingStop * _Point, PositionGetDouble(POSITION_TP)); } } } } } } //+------------------------------------------------------------------+ //HAPPY DEVELOPING!
达到这一阶段后,我们可以继续对我们的程序进行测试。我在下面附上了我的测试经验。
测试器
为了在MetaTrader 5的策略测试器中测试“趋势约束EA”,我们将首先使用历史数据设置一个回测,以评估EA的性能。这一过程将使我们能够在各种市场条件下模拟其交易策略,从而帮助分析盈利能力、风险管理以及整体有效性。我们需要选择我们期望的时间框架(在本例中为M1时间框架)、输入参数和交易环境,并观察EA是否严格遵循趋势跟踪逻辑和RSI条件。这次测试对于在考虑实盘交易之前对EA进行微调至关重要。我是一个Boom 500指数的粉丝,我喜欢在这个美丽的货币对上测试EA。
策略测试器的设置:趋势约束EA
在策略测试器中的测试表现
测试结果 01/2023-12/2023
结论
我很高兴,我们已经完成了这次精彩的类似教学的讨论。我们的目标是创建一个独立的EA,它不需要安装任何特定的指标。我希望这次讨论能够启发你理解EA开发的结构,并为你提供一个坚实的起点。我们专注于最基本的概念,以确保基础易于掌握。通过输入各种因素来定制EA的能力,允许你尝试不同的值,以找到最具盈利性的设置。
我们已经成功开发了一个基于我们最初想法的可运行的EA,并且我们可以在测试器中观察订单的执行情况。然而,仍有很大的改进空间。趋势约束EA需要进一步完善,特别是在其入场条件方面,以提高与当前日线趋势一致的盈利能力。这一次,我们没有包含一个“Magic数字”,在真实模拟账户上测试时,我意识到EA影响了已经存在的其他订单。
与我们之前依赖于安装指标的开发相比,这次开发的一个优势是我们现在有了一个可移植的独立文件来运行。这并不意味着我们之前的尝试是徒劳的;相反,我们从中吸取了宝贵的教训,而那个项目的工具仍然对未来的工作很有用。我们的目标是继续完善EA,以实现最佳可能的结果,随着我们系列的推进。
接下来,我们将加入一个“Magic数字”,增强我们的入场技术,并进一步提升我们在开发中的创造力。祝您交易愉快!
文件 | 说明 |
---|---|
Trend Constraint Expert.mq5 | 源代码。 |
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/15322
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。

