
如何在MQL5的EA中实现自优化
准备好进入自优化外汇交易算法的奇妙世界。它可以让您的EA根据交易完成后市场条件的变化,为下一轮交易进行自我调整。
将您的EA视为一个通过移动平均线观察趋势的精明交易者。虽然它确实有效,但是如果它是一个能够随着时间学习调整策略的市场感知型黑箱,那会怎样?这就是自优化的过程。
使用EA的主要优势之一是,它最终能够随着市场条件的持续变化而进行调整。EA能够自动适应当前的市场环境,减少持续的人工监督和参数更改。这使得交易者能够持续利用秒级的短期机会,并不间断地执行其交易策略。此外,EA还能够在全天候的交易中不断调整交易策略。
然而,也有一些需要注意的陷阱。一个挑战是过度拟合最近数据的风险,这可能导致在不同市场条件下表现不佳。有效管理计算资源也很关键,因为自动化策略时代码复杂性可能会增加。在参数变化期间保持稳定性可能很难,并且有时归因于性能可能成为一个问题。
在本指南中,我们将探讨构建自优化EA的过程,包括使用自定义指标自动化策略。我们将涵盖稳健的优化逻辑、参数选择的最佳实践,以及如何通过回测重构策略。此外,还将讨论诸如分步优化等高级方法,以增强您的交易方法。
如何实现目标,保持高效(以及如何避免陷阱!)
我们将基于移动平均线交叉策略制定交易计划,这是最基础的策略,但在趋势市场中从未失效。
1. 为自优化EA设置库、输入参数和优化范围
1.1 导入所需的库
在第一行中包含必要的MQL5库:
#include <Trade\Trade.mqh> #include <Arrays\ArrayObj.mqh>
Trade库提供了执行交易的函数,而ArrayObj库允许我们处理对象的动态数组,我们将使用它来存储优化结果。
1.2 定义输入参数
接下来,我们定义EA的输入参数:
input int MA_Fast_Period = 10; // Fast Moving Average Period input int MA_Slow_Period = 20; // Slow Moving Average Period input ENUM_MA_METHOD MA_Method = MODE_SMA; // Moving Average Method input ENUM_APPLIED_PRICE Applied_Price = PRICE_CLOSE; // Applied Price input double LotSize = 0.1; // Lot Size input int StopLoss = 50; // Stop Loss in points input int TakeProfit = 100; // Take Profit in points // Optimization parameters input bool AutoOptimize = false; // Enable Auto Optimization input int OptimizationPeriod = 5000; // Number of ticks between optimizations input int MinDataPoints = 1000; // Minimum number of data points for optimization
这些输入参数允许用户直接从MetaTrader界面配置EA的行为和优化设置。
1.3 全局变量和句柄
然后,我们声明将在整个EA中使用的全局变量和句柄:
CTrade trade; int fastMA_Handle, slowMA_Handle; double fastMA[], slowMA[]; int tickCount = 0; CArrayObj* optimizationResults; // Optimization ranges const int MA_Fast_Min = 5, MA_Fast_Max = 50, MA_Fast_Step = 1; const int MA_Slow_Min = 10, MA_Slow_Max = 100, MA_Slow_Step = 1;
CTrade对象用于处理交易操作,而fastMA_Handle和slowMA_Handle用于管理移动平均线指标。optimizationResults数组将存储我们优化测试的结果。
1.4 优化设置
优化设置定义了我们将为每个参数测试的值的范围:
- 快速移动平均线周期:从5到50,步长为1;慢速移动平均线周期:从10到100,步长为1
2. 实现核心交易逻辑
在我们的EA结构设置好之后,让我们实现将要处理初始化、反初始化和tick处理的核心函数。
2.1 OnInit()函数
当EA首次被加载到图表上时,会调用OnInit()函数。我们这样来实现:
int OnInit() { // Initialize MA handles fastMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Fast_Period, 0, MA_Method, Applied_Price); slowMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Slow_Period, 0, MA_Method, Applied_Price); if(fastMA_Handle == INVALID_HANDLE || slowMA_Handle == INVALID_HANDLE) { Print("Failed to create MA indicators"); return INIT_FAILED; } // Initialize optimization results array optimizationResults = new CArrayObj(); return INIT_SUCCEEDED; }
该函数创建了移动平均线指标并初始化了优化结果数组。如果指标初始化失败,EA将无法启动。
2.2 OnDeinit() 函数
当EA从图表中移除或终端关闭时,会调用OnDeinit()函数:void OnDeinit(const int reason) { // Release MA handles IndicatorRelease(fastMA_Handle); IndicatorRelease(slowMA_Handle); // Clean up optimization results if(optimizationResults != NULL) { delete optimizationResults; optimizationResults = NULL; } }
该函数确保我们正确释放指标句柄,并释放优化结果数组所使用的内存。
2.3 OnTick() 函数:
OnTick() 函数是我们EA的核心,会在所选交易品种的每个tick时被调用:
void OnTick() { // Check if we have enough bars to calculate MAs if(Bars(_Symbol, PERIOD_CURRENT) < MA_Slow_Period) return; // Copy MA values if(CopyBuffer(fastMA_Handle, 0, 0, 2, fastMA) != 2) return; if(CopyBuffer(slowMA_Handle, 0, 0, 2, slowMA) != 2) return; // Auto Optimization if(AutoOptimize && ++tickCount >= OptimizationPeriod) { Optimize(); tickCount = 0; } // Trading logic if(fastMA[1] <= slowMA[1] && fastMA[0] > slowMA[0]) { // Open buy position double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); trade.Buy(LotSize, _Symbol, ask, ask - StopLoss * _Point, ask + TakeProfit * _Point); } else if(fastMA[1] >= slowMA[1] && fastMA[0] < slowMA[0]) { // Open sell position double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); trade.Sell(LotSize, _Symbol, bid, bid + StopLoss * _Point, bid - TakeProfit * _Point); } }
该函数执行几个关键任务:
- 检查是否有足够的历史数据来计算移动平均线。
- 获取移动平均线的当前值。
- 如果启用了自优化,并且到了优化的时间(基于tick计数),就会调用Optimize()函数。
- 实现了交易逻辑,根据移动平均线的交叉来开多仓或空仓。
3. 实现优化逻辑
我们自优化功能的核心在于Optimize()函数。让我们来分解并检查其组成部分。
3.1 Optimize() 函数
以下是Optimize()函数的整体结构:
void Optimize() { Print("Starting optimization..."); optimizationResults.Clear(); // Loop through all combinations of MA periods for(int fastPeriod = MA_Fast_Min; fastPeriod <= MA_Fast_Max; fastPeriod += MA_Fast_Step) { for(int slowPeriod = MA_Slow_Min; slowPeriod <= MA_Slow_Max; slowPeriod += MA_Slow_Step) { if(slowPeriod <= fastPeriod) continue; // Slow period should be greater than fast period double profit = TestParameters(fastPeriod, slowPeriod); OptimizationResult* result = new OptimizationResult; result.fastPeriod = fastPeriod; result.slowPeriod = slowPeriod; result.profit = profit; optimizationResults.Add(result); } } // Find the best result OptimizationResult* bestResult = NULL; for(int i = 0; i < optimizationResults.Total(); i++) { OptimizationResult* currentResult = optimizationResults.At(i); if(bestResult == NULL || currentResult.profit > bestResult.profit) { bestResult = currentResult; } } if(bestResult != NULL) { // Update the EA parameters MA_Fast_Period = bestResult.fastPeriod; MA_Slow_Period = bestResult.slowPeriod; // Update indicator handles IndicatorRelease(fastMA_Handle); IndicatorRelease(slowMA_Handle); fastMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Fast_Period, 0, MA_Method, Applied_Price); slowMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Slow_Period, 0, MA_Method, Applied_Price); Print("Optimization complete. New parameters: Fast MA = ", MA_Fast_Period, ", Slow MA = ", MA_Slow_Period); } else { Print("Optimization failed to find better parameters."); } }
3.2 遍历参数组合
Optimize()函数中的嵌套for循环使我们能够测试在指定范围内所有可能的快速和慢速移动平均线周期的组合。这被称为“暴力”优化方法。
for(int fastPeriod = MA_Fast_Min; fastPeriod <= MA_Fast_Max; fastPeriod += MA_Fast_Step) { for(int slowPeriod = MA_Slow_Min; slowPeriod <= MA_Slow_Max; slowPeriod += MA_Slow_Step) { if(slowPeriod <= fastPeriod) continue; // Slow period should be greater than fast period double profit = TestParameters(fastPeriod, slowPeriod); // Store results... } }
我们会跳过那些慢速周期小于或等于快速周期的组合,因为其对策略来说是没有意义的。
3.3 存储和比较结果
对于每组有效的组合,我们调用TestParameters()来评估其性能。其结果存储于一个OptimizationResult对象中,并添加到我们的optimizationResults数组中。
在测试完所有组合后,我们遍历结果以找到表现最优的参数集:
OptimizationResult* bestResult = NULL; for(int i = 0; i < optimizationResults.Total(); i++) { OptimizationResult* currentResult = optimizationResults.At(i); if(bestResult == NULL || currentResult.profit > bestResult.profit) { bestResult = currentResult; } }
如果找到了最优结果,我们将更新EA的参数,并使用新的周期重新创建指标句柄。
4. 测试参数
TestParameters()函数对于评估每组参数至关重要。让我们来详细测一测。
4.1 TestParameters() 函数
double TestParameters(int fastPeriod, int slowPeriod) { int maFast = iMA(_Symbol, PERIOD_CURRENT, fastPeriod, 0, MA_Method, Applied_Price); int maSlow = iMA(_Symbol, PERIOD_CURRENT, slowPeriod, 0, MA_Method, Applied_Price); if(maFast == INVALID_HANDLE || maSlow == INVALID_HANDLE) { Print("Failed to create MA indicators for testing"); return -DBL_MAX; } double fastBuffer[], slowBuffer[]; ArraySetAsSeries(fastBuffer, true); ArraySetAsSeries(slowBuffer, true); int copied = CopyBuffer(maFast, 0, 0, MinDataPoints, fastBuffer); copied = MathMin(copied, CopyBuffer(maSlow, 0, 0, MinDataPoints, slowBuffer)); if(copied < MinDataPoints) { Print("Not enough data for testing"); return -DBL_MAX; } double profit = 0; for(int i = 1; i < copied; i++) { if(fastBuffer[i] > slowBuffer[i] && fastBuffer[i-1] <= slowBuffer[i-1]) { // Buy signal profit += Close[i-1] - Open[i]; } else if(fastBuffer[i] < slowBuffer[i] && fastBuffer[i-1] >= slowBuffer[i-1]) { // Sell signal profit += Open[i] - Close[i-1]; } } IndicatorRelease(maFast); IndicatorRelease(maSlow); return profit; }
4.2 创建临时指标
对于测试的每组参数,我们创建临时的移动平均线指标:
int maFast = iMA(_Symbol, PERIOD_CURRENT, fastPeriod, 0, MA_Method, Applied_Price); int maSlow = iMA(_Symbol, PERIOD_CURRENT, slowPeriod, 0, MA_Method, Applied_Price);
这些临时指标使我们能够在不影响主交易逻辑的情况下,计算不同周期的移动平均线。
4.3 模拟交易
然后,我们遍历历史数据,根据我们的移动平均线交叉逻辑模拟交易:
for(int i = 1; i < copied; i++) { if(fastBuffer[i] > slowBuffer[i] && fastBuffer[i-1] <= slowBuffer[i-1]) { // Buy signal profit += Close[i-1] - Open[i]; } else if(fastBuffer[i] < slowBuffer[i] && fastBuffer[i-1] >= slowBuffer[i-1]) { // Sell signal profit += Open[i] - Close[i-1]; } }
这种简化的模拟假设我们可以在交叉信号之后的K线开盘价开仓,并在该K线收盘价平仓。
4.4 计算利润
该函数返回模拟交易产生的总利润。在更复杂的实现中,您可能会考虑其他因素,如最大回撤、夏普比率或胜率。
5. 应用优化后的参数
找到表现最优参数后,我们需要将它们应用到EA中。
5.1 更新EA参数
我们用新的最优值更新全局变量:
MA_Fast_Period = bestResult.fastPeriod; MA_Slow_Period = bestResult.slowPeriod;
5.2 重新创建指标句柄
在更新参数之后,我们需要重新创建我们的指标句柄:
IndicatorRelease(fastMA_Handle); IndicatorRelease(slowMA_Handle); fastMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Fast_Period, 0, MA_Method, Applied_Price); slowMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Slow_Period, 0, MA_Method, Applied_Price);
这样确保了我们后续的主要交易逻辑将使用新优化后的参数。
6. 高级优化技术
尽管我们当前的实现为自优化提供了坚实的基础,但您可以考虑几种高级技术,以便进一步提升EA的性能。
6.1 多标准优化
除了仅针对利润进行优化外,您还可以将多个标准纳入优化过程。例如:
struct OptimizationResult { int fastPeriod; int slowPeriod; double profit; double drawdown; double sharpeRatio; }; double CalculateScore(const OptimizationResult &result) { return result.profit * 0.5 + result.sharpeRatio * 0.3 - result.drawdown * 0.2; }
该方法能够让您在性能的多个方面之间取得平衡,从而有可能会得到更稳健的参数集。
6.2 分步优化
分步优化涉及将您的历史数据分成多个部分,在一个部分上进行优化,然后在下一个部分上进行测试。
这有助于防止过拟合:
void WalkForwardOptimization() { int totalBars = Bars(_Symbol, PERIOD_CURRENT); int segmentSize = 1000; // Adjust as needed for(int i = 0; i < totalBars - 2*segmentSize; i += segmentSize) { // Optimize on segment i to i+segmentSize OptimizeSegment(i, i+segmentSize); // Test on segment i+segmentSize to i+2*segmentSize TestSegment(i+segmentSize, i+2*segmentSize); } }
6.3 自适应参数调整
与其在优化过程中完全替换参数,不如实现一个系统,能够根据近期表现分步调整参数:
void AdaptParameters() { double recentPerformance = CalculateRecentPerformance(); double adaptationRate = 0.1; // Adjust as needed MA_Fast_Period += (int)((bestResult.fastPeriod - MA_Fast_Period) * adaptationRate * recentPerformance); MA_Slow_Period += (int)((bestResult.slowPeriod - MA_Slow_Period) * adaptationRate * recentPerformance); }
该方法能够在不同参数组合之间实现更为平滑的过渡,并有可能降低短期市场噪音对优化过程的影响。
7. 最佳实践与注意事项
在实现并改进您的自优化型EA时,请牢记以下最佳做法:
7.1 选择优化频率
优化的频率会显著影响您的EA的表现。过于频繁的优化可能导致对短期市场波动的过度反应,而优化频率过低则可能错失交易机会。
建议根据市场波动性或EA近期的表现来设定动态的优化频率:
bool ShouldOptimize() { double recentVolatility = CalculateRecentVolatility(); int dynamicPeriod = (int)(OptimizationPeriod * (1 + recentVolatility)); return tickCount >= dynamicPeriod; }
7.2 平衡适应性与稳定性
尽管适应性是自优化型EA的一大核心优势,但保持一定程度的稳定性同样至关重要。若参数发生剧烈变化,可能导致交易行为缺乏一致性。
建议为单次优化过程中参数调整幅度设定阈值:
void LimitParameterChange(int ¶meter, int newValue, int maxChange) { int change = newValue - parameter; change = MathMax(-maxChange, MathMin(change, maxChange)); parameter += change; }
7.3 过拟合是任何优化过程中的一大风险。过度拟合的EA在历史数据上可能表现异常出色,但在面对新的市场条件时可能会导致失败。为了降低这种风险,使用足够量的历史数据进行优化非常重要。此外,实施样本外测试或分步优化可以帮助确保稳健性。您的策略的复杂性也应该与可用数据量相对应。密切监控实时表现至关重要,如果EA的行为与回测结果有显著偏差,您必须准备好进行干预。
7.4 自优化可能会非常耗费计算资源,因此确保计算效率是关键。为了保持EA的响应性,建议在非交易时段运行优化,或者在单独的线程上运行。高效的数据结构和算法可以大大减少处理时间。对于更复杂的优化任务,基于云的解决方案可能是一个不错的选择。
8. 开发自优化EA在调试和故障排除方面带来新挑战
8.1 在开发过程中可能会出现一些常见问题。例如,如果您的EA在每次运行时都产生不一致的结果,请确保您使用的是相同的数据,并检查代码中是否有任何随机元素。尽管回测结果良好,但实时表现不佳,可能是由于滑点、点差或变化的市场条件等因素造成的。如果优化失败或产生意想不到的结果,重要的一点是检查您的优化标准,并确保您的TestParameters()函数按预期工作。
8.2 为了帮助调试,强烈建议记录所有日志。这将使您能够详细跟踪EA的行为和优化过程,有助于更有效地识别和解决问题。
记录所有日志以跟踪EA的行为和优化过程:void Log(string message) { Print(TimeToString(TimeCurrent()) + ": " + message); int handle = FileOpen("EA_Log.txt", FILE_WRITE|FILE_READ|FILE_TXT); if(handle != INVALID_HANDLE) { FileSeek(handle, 0, SEEK_END); FileWriteString(handle, TimeToString(TimeCurrent()) + ": " + message + "\n"); FileClose(handle); } }
使用该函数来记录重要事件、参数变更以及在运行或优化过程中出现的任何错误。
8.3 MetaTrader策略测试器是调试EA的宝贵工具。您可以使用视觉模式逐根K线分步跟踪EA的行为,这样可以详细地了解EA如何实时运行。此外,将策略测试器中的优化结果与EA的自优化结果进行比较,有助于识别任何差异。策略测试器的优化功能也是验证TestParameters()函数功能的一种有用方法。
9. 自优化移动平均线EA的性能和挑战
9.1 为了说明自优化的潜在好处和挑战,让我们考虑一个假设性的案例研究。
我们在2010年至2020年的欧元兑美元1小时图数据上对自优化移动平均线交叉EA进行了回测。配置EA为每5000个tick优化其参数。结果令人欣慰,显示出1247笔交易的总净盈利为15,420美元,盈利因子为1.65,最大回撤为2,105美元。
然后,我们将这些结果与使用静态参数(MA_Fast_Period = 10,MA_Slow_Period = 20)的相同EA进行比较。静态版本产生了8,750美元的总净盈利,盈利因子为1.38,从1,562笔总交易中产生了更高的3,210美元的最大回撤。这种比较表明,自优化版本显著提高了整体盈利能力和经风险调整后的回报。
9.2 尽管回测结果令人鼓舞,但在评估实际表现时,有几个重要因素需要考虑。市场环境的变化,如2008年金融危机或2020年COVID-19大流行,可能会影响EA的适应性,这需要仔细评估。此外,应考虑交易成本,以确保改进的表现能够证明任何增加的交易活动是合理的。在实时环境中运行连续优化所需的计算资源是另一个需要考虑的因素,以及在EA适应新环境期间,应对表现不佳时所需具备的心理承受能力也同样不容忽视。
10. 自优化EA的未来趋势:机器学习、外部数据和云解决方案
10.1 随着交易技术的不断发展,自优化EA领域出现了令人兴奋的发展趋势。其中之一是将机器学习算法整合到外汇交易中,这可以通过识别市场数据中的复杂模式来增强优化过程。这为未来的交易策略提供了更具适应性和更高效的机会。
机器学习算法可以通过识别市场数据中的复杂模式来增强优化过程:
from sklearn.ensemble import RandomForestRegressor def ml_optimize(data, labels): model = RandomForestRegressor(n_estimators=100) model.fit(data, labels) return model.feature_importances_
虽然该实例使用了Python,但类似的机器学习技术也可以在MQL5中实现,或者通过外部库进行集成。
10.2 与外部数据源的整合
将外部数据(经济指标、情绪分析等)纳入优化过程,可以提供更全面的市场状况视图:
string GetExternalData() { string cookie=NULL,headers; char post[],result[]; int res; string url="https://api.example.com/economic-data"; res=WebRequest("GET",url,cookie,NULL,500,post,0,result,headers); if(res==-1) { Print("Error in WebRequest. Error code =",GetLastError()); return ""; } string resultString=CharArrayToString(result); return resultString; }
通过整合外部数据,您的EA可以更明智地决定何时以及如何优化其参数。
10.3 基于云的优化
随着优化任务变得越来越复杂,基于云的解决方案为更强大和灵活的优化过程提供了可能:
void CloudOptimize() { string optimizationData = PrepareOptimizationData(); string url = "https://your-cloud-service.com/optimize"; string headers = "Content-Type: application/json\r\n"; char post[], result[]; string resultHeaders; StringToCharArray(optimizationData, post); int res = WebRequest("POST", url, headers, 30000, post, result, resultHeaders); if(res == -1) { Print("Error in WebRequest. Error code =", GetLastError()); return; } string optimizationResult = CharArrayToString(result); ApplyCloudOptimizationResult(optimizationResult); }
这种方法能够使您利用比本地计算机更强大的计算能力,并有可能采用在本地计算机上或许难以实现的、更为复杂精细的优化算法。
开发有效的自优化EA是一个持续的过程,需要不断的改进和学习。一种改进策略是定期审查EA的性能。此做法将有助于识别需要改进的领域,并确保EA在变化的市场条件下继续表现最优。
void WeeklyPerformanceReview() { datetime startOfWeek = iTime(_Symbol, PERIOD_W1, 0); double weeklyProfit = 0; int totalTrades = 0; for(int i = 0; i < HistoryDealsTotal(); i++) { ulong ticket = HistoryDealGetTicket(i); if(HistoryDealGetInteger(ticket, DEAL_TIME) >= startOfWeek) { weeklyProfit += HistoryDealGetDouble(ticket, DEAL_PROFIT); totalTrades++; } } Print("Weekly Performance: Profit = ", weeklyProfit, ", Trades = ", totalTrades); }
利用这些审查来识别改进的领域以及优化过程中可能出现的问题。
保持对交易技术、市场动态和法规的了解至关重要。全面的测试,包括样本外测试和前瞻性测试,是关键。持续学习和监控是长期成功的关键。尽管功能强大,自优化应补充健全交易原则和风险管理。保持好奇心和谨慎,优先保护你的资本。
祝您交易顺利!
对于那些希望进一步了解自优化和算法交易的人来说,此处有一些附加的资源:
- 《构建可靠的交易系统》 作者:Keith Fitschen
- 《算法交易:获胜策略及其理由》作者:Ernie Chan
- 《机器学习在算法交易中的应用》 作者:Stefan Jansen
- MQL5文档:MQL5文档
- 外汇工厂论坛 - 编程子论坛:外汇工厂论坛
- Quantopian课程:Quantopian课程
11. EA示例代码
以下是自优化EA的完整代码。将此代码复制到您的MetaEditor中并编译,以便在MetaTrader 5中使用。
//+------------------------------------------------------------------+ //| Auto-Optimizing Moving Average Crossover EA | //| Copyright 2024, Javier Santiago Gaston de Iriarte Cabrera | //| https://www.mql5.com/en/users/jsgaston/news | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Javier Santiago Gaston de Iriarte Cabrera" #property link "https://www.mql5.com/en/users/jsgaston/news" #property version "1.00" #property strict // Include necessary libraries #include <Trade\Trade.mqh> #include <Arrays\ArrayObj.mqh> // Input parameters input ENUM_MA_METHOD MA_Method = MODE_SMA; // Moving Average Method input ENUM_APPLIED_PRICE Applied_Price = PRICE_CLOSE; // Applied Price input double LotSize = 0.01; // Lot Size input int StopLoss = 100; // Stop Loss in points input int TakeProfit = 200; // Take Profit in points input int Initial_MA_Fast_Period = 10; // Initial Fast Moving Average Period input int Initial_MA_Slow_Period = 20; // Initial Slow Moving Average Period // Optimization parameters input bool AutoOptimize = true; // Enable Auto Optimization input int OptimizationPeriod = 5000; // Number of ticks between optimizations input int MinDataPoints = 1000; // Minimum number of data points for optimization // Global variables CTrade trade; int fastMA_Handle, slowMA_Handle; double fastMA[], slowMA[]; int tickCount = 0; CArrayObj optimizationResults; int MA_Fast_Period, MA_Slow_Period; // Optimization ranges const int MA_Fast_Min = 5, MA_Fast_Max = 50, MA_Fast_Step = 1; const int MA_Slow_Min = 10, MA_Slow_Max = 100, MA_Slow_Step = 1; // Class to hold optimization results class OptimizationResult : public CObject { public: int fastPeriod; int slowPeriod; double profit; }; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { MA_Fast_Period = Initial_MA_Fast_Period; MA_Slow_Period = Initial_MA_Slow_Period; // Initialize MA handles fastMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Fast_Period, 0, MA_Method, Applied_Price); slowMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Slow_Period, 0, MA_Method, Applied_Price); if(fastMA_Handle == INVALID_HANDLE || slowMA_Handle == INVALID_HANDLE) { Print("Failed to create MA indicators"); return INIT_FAILED; } return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Release MA handles IndicatorRelease(fastMA_Handle); IndicatorRelease(slowMA_Handle); // Clean up optimization results optimizationResults.Clear(); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Check if we have enough bars to calculate MAs if(Bars(_Symbol, PERIOD_CURRENT) < MA_Slow_Period) return; // Copy MA values if(CopyBuffer(fastMA_Handle, 0, 0, 2, fastMA) != 2) return; if(CopyBuffer(slowMA_Handle, 0, 0, 2, slowMA) != 2) return; // Auto Optimization if(AutoOptimize && ++tickCount >= OptimizationPeriod) { Optimize(); tickCount = 0; } // Trading logic if(fastMA[1] <= slowMA[1] && fastMA[0] > slowMA[0]) { // Open buy position double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); trade.Buy(LotSize, _Symbol, ask, ask - StopLoss * _Point, ask + TakeProfit * _Point); } else if(fastMA[1] >= slowMA[1] && fastMA[0] < slowMA[0]) { // Open sell position double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); trade.Sell(LotSize, _Symbol, bid, bid + StopLoss * _Point, bid - TakeProfit * _Point); } } //+------------------------------------------------------------------+ //| Optimization function | //+------------------------------------------------------------------+ void Optimize() { Print("Starting optimization..."); optimizationResults.Clear(); // Loop through all combinations of MA periods for(int fastPeriod = MA_Fast_Min; fastPeriod <= MA_Fast_Max; fastPeriod += MA_Fast_Step) { for(int slowPeriod = MA_Slow_Min; slowPeriod <= MA_Slow_Max; slowPeriod += MA_Slow_Step) { if(slowPeriod <= fastPeriod) continue; // Slow period should be greater than fast period double profit = TestParameters(fastPeriod, slowPeriod); OptimizationResult* result = new OptimizationResult(); result.fastPeriod = fastPeriod; result.slowPeriod = slowPeriod; result.profit = profit; optimizationResults.Add(result); } } // Find the best result OptimizationResult* bestResult = NULL; for(int i = 0; i < optimizationResults.Total(); i++) { OptimizationResult* currentResult = optimizationResults.At(i); if(bestResult == NULL || currentResult.profit > bestResult.profit) { bestResult = currentResult; } } if(bestResult != NULL) { // Update the EA parameters MA_Fast_Period = bestResult.fastPeriod; MA_Slow_Period = bestResult.slowPeriod; // Update indicator handles IndicatorRelease(fastMA_Handle); IndicatorRelease(slowMA_Handle); fastMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Fast_Period, 0, MA_Method, Applied_Price); slowMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Slow_Period, 0, MA_Method, Applied_Price); Print("Optimization complete. New parameters: Fast MA = ", MA_Fast_Period, ", Slow MA = ", MA_Slow_Period); } else { Print("Optimization failed to find better parameters."); } } //+------------------------------------------------------------------+ //| Test a set of parameters | //+------------------------------------------------------------------+ double TestParameters(int fastPeriod, int slowPeriod) { int maFast = iMA(_Symbol, PERIOD_CURRENT, fastPeriod, 0, MA_Method, Applied_Price); int maSlow = iMA(_Symbol, PERIOD_CURRENT, slowPeriod, 0, MA_Method, Applied_Price); if(maFast == INVALID_HANDLE || maSlow == INVALID_HANDLE) { Print("Failed to create MA indicators for testing"); return -DBL_MAX; } double fastBuffer[], slowBuffer[]; ArraySetAsSeries(fastBuffer, true); ArraySetAsSeries(slowBuffer, true); int copied = CopyBuffer(maFast, 0, 0, MinDataPoints, fastBuffer); copied = MathMin(copied, CopyBuffer(maSlow, 0, 0, MinDataPoints, slowBuffer)); if(copied < MinDataPoints) { Print("Not enough data for testing"); return -DBL_MAX; } double Close[], Open[]; ArraySetAsSeries(Close, true); ArraySetAsSeries(Open, true); copied = CopyClose(_Symbol, PERIOD_CURRENT, 0, copied, Close); copied = MathMin(copied, CopyOpen(_Symbol, PERIOD_CURRENT, 0, copied, Open)); double profit = 0; for(int i = 1; i < copied; i++) { if(fastBuffer[i] > slowBuffer[i] && fastBuffer[i-1] <= slowBuffer[i-1]) { // Buy signal profit += Close[i-1] - Open[i]; } else if(fastBuffer[i] < slowBuffer[i] && fastBuffer[i-1] >= slowBuffer[i-1]) { // Sell signal profit += Open[i] - Close[i-1]; } } IndicatorRelease(maFast); IndicatorRelease(maSlow); return profit; } //+------------------------------------------------------------------+ //| Custom function to log important events | //+------------------------------------------------------------------+ void Log(string message) { Print(TimeToString(TimeCurrent()) + ": " + message); int handle = FileOpen("EA_Log.txt", FILE_WRITE|FILE_READ|FILE_TXT); if(handle != INVALID_HANDLE) { FileSeek(handle, 0, SEEK_END); FileWriteString(handle, TimeToString(TimeCurrent()) + ": " + message + "\n"); FileClose(handle); } }
如何使用此EA
- 将整个代码复制到MetaEditor中的一个新文件中。
- 将其保存为具有.mq5扩展名的文件(例如,“AutoOptimizingMA.mq5”)。
- 通过点击“编译”按钮或按下F7键来编译EA。
- 在MetaTrader 5中,将编译后的EA拖放到图表上。
- 根据需要在EA的设置窗口中调整输入参数。
- 启用自动交易,并运行EA。
此EA的关键特性
- 移动平均线交叉策略:EA使用基本的移动平均线交叉策略来进行交易决策。
- 自优化:EA可以根据最近的市场数据自优化其参数(快速和慢速移动平均线周期)。
- 可定制输入:用户可以调整各种参数,包括手数、止损、获利和优化设置。
- 性能日志记录:EA包含一个日志记录功能,用于跟踪重要事件和参数变化。
重要提示
- 此EA作为教学示例提供,未经全面测试和定制,不应用于实盘交易。自优化可能会非常耗费计算资源。注意系统资源,尤其是在VPS或本地计算机上运行时。
- 直到在模拟环境中全面测试EA后,才考虑实盘交易。
- 过去的性能并不能保证未来的结果。市场条件可能会发生变化,从而可能影响EA的性能。
通过使用此EA,您可以探索自优化在实践中是如何工作的,并可能提高您的交易策略适应变化的市场条件的能力。请记得要持续监控其性能,并根据需要进行调整。
您可以通过添加更多条件来改进订单,例如添加深度学习或RSI,或者任何您能想到的条件,从而可能获得更好的结果。
请记住,算法交易的世界是广阔的且持续发展的。本指南是您进入自优化EA领域的起点。随着经验的积累和深入地理解,您无疑会发现新的技术与方法来完善和提升您的交易系统。
祝您好运,愿您的交易保持盈利!
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/15837


