
基于MQL5的订单剥头皮交易系统
引言
适用于MetaTrader 5(MQL5)的复杂算法交易系统示例,采用剥头皮订单流技术的EA。
剥头皮订单流是一种短期交易策略,专注于通过分析订单的实时流动来识别市场的潜在入场和出场点。它通过结合成交量、价格走势和订单簿数据的研究,快速做出交易决策。通常,持仓时间非常短暂——往往仅持续几分钟甚至几秒钟。
该EA通过使用多种技术指标和市场分析方法,基于订单流失衡寻找交易机会。它还包含高级风险管理功能,例如跟踪止损、部分平仓和动态仓位调整。此外,EA还引入了在重大新闻事件期间禁止交易的方法,并设置了连续亏损的限制。
订单流交易的基本理念是通过研究实时订单簿数据和成交量动态来预测短期价格波动。该EA将这一理念与其他成熟的技术分析指标相结合,形成了一个混合策略,旨在识别高概率的交易机会。
该EA的另一个主要特点是其对风险管理的重视。在外汇交易的动荡世界中,尤其是在使用剥头皮策略时,有效的风险控制至关重要。为了保护资本并优化潜在回报,该系统包括跟踪止损、部分平仓方法和动态仓位调整。
由于其灵活的设计,交易者可以根据自己的交易风格和风险承受能力调整EA的参数。用户可以根据自己的交易目标和市场观点调整系统的多个方面,例如成交量阈值和指标周期。
重要的是要明白,尽管该EA可以自动交易,但它并不是一种“设置后就不用管”的解决方案。用户需要熟悉外汇交易的基础知识、订单流的理念以及该系统中包含的特定指标。建议定期监控并根据不同的市场情况做出必要的调整,以确保EA始终保持最佳运行状态。
代码
该EA专为MetaTrader 5设计,实现了带有复杂风险管理功能的高级订单流剥头皮策略。在OnInit()函数中,EA初始化各种技术指标并验证输入参数。它为移动平均线、ADX、ATR、RSI和布林带等指标设置句柄。
#include <Trade\Trade.mqh> #include <Trade\PositionInfo.mqh> // Input parameters input int VolumeThreshold = 35000; // Volume threshold to consider imbalance input int OrderFlowPeriod = 30; // Number of candles to analyze order flow input double RiskPercent = 1.0; // Risk percentage per trade input int ADXPeriod = 14; // ADX Period input int ADXThreshold = 25; // ADX threshold for strong trend input int MAPeriod = 200; // Moving Average Period input ENUM_TIMEFRAMES Timeframe = PERIOD_M15; // Timeframe for analysis input double MaxLotSize = 0.1; // Maximum allowed lot size input int ATRPeriod = 14; // ATR Period input double ATRMultiplier = 2.0; // ATR Multiplier input int RSIPeriod = 14; // RSI Period input int RSIOverbought = 70; // RSI Overbought level input int RSIOversold = 30; // RSI Oversold level input int MAFastPeriod = 10; // Fast Moving Average Period input int MASlowPeriod = 30; // Slow Moving Average Period input int BollingerPeriod = 20; // Bollinger Bands Period input double BollingerDeviation = 2.5; // Bollinger Bands Standard Deviation input int MaxConsecutiveLosses = 1; // Maximum number of consecutive losses before pausing input int MinBarsBetweenTrades = 1; // Minimum number of bars between trades // Global variables CTrade trade; CPositionInfo positionInfo; int maHandle, adxHandle, atrHandle, rsiHandle, maFastHandle, maSlowHandle, bollingerHandle; int consecutiveLosses = 0; datetime lastTradeTime = 0; int barsSinceLastTrade = 0; // New global variables for statistics int totalTrades = 0; int winningTrades = 0; double totalProfit = 0;
int OnInit() { // Logging initialization Print("Starting Order Flow EA v13..."); // Verify trading permissions if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) { Print("Error: Automated trading is not allowed in the terminal."); return INIT_FAILED; } if(!MQLInfoInteger(MQL_TRADE_ALLOWED)) { Print("Error: Automated trading is not allowed for this EA."); return INIT_FAILED; } // Initialize trading object trade.SetExpertMagicNumber(123456); trade.SetMarginMode(); trade.SetTypeFillingBySymbol(_Symbol); trade.SetDeviationInPoints(10); // 1 pip deviation allowed Print("Trading object initialized."); // Initialize indicators maHandle = iMA(_Symbol, Timeframe, MAPeriod, 0, MODE_SMA, PRICE_CLOSE); adxHandle = iADX(_Symbol, Timeframe, ADXPeriod); atrHandle = iATR(_Symbol, Timeframe, ATRPeriod); rsiHandle = iRSI(_Symbol, Timeframe, RSIPeriod, PRICE_CLOSE); maFastHandle = iMA(_Symbol, Timeframe, MAFastPeriod, 0, MODE_EMA, PRICE_CLOSE); maSlowHandle = iMA(_Symbol, Timeframe, MASlowPeriod, 0, MODE_EMA, PRICE_CLOSE); bollingerHandle = iBands(_Symbol, Timeframe, BollingerPeriod, 0, BollingerDeviation, PRICE_CLOSE); // Verify indicator initialization if(maHandle == INVALID_HANDLE || adxHandle == INVALID_HANDLE || atrHandle == INVALID_HANDLE || rsiHandle == INVALID_HANDLE || maFastHandle == INVALID_HANDLE || maSlowHandle == INVALID_HANDLE || bollingerHandle == INVALID_HANDLE) { Print("Error initializing indicators:"); if(maHandle == INVALID_HANDLE) Print("- Invalid MA"); if(adxHandle == INVALID_HANDLE) Print("- Invalid ADX"); if(atrHandle == INVALID_HANDLE) Print("- Invalid ATR"); if(rsiHandle == INVALID_HANDLE) Print("- Invalid RSI"); if(maFastHandle == INVALID_HANDLE) Print("- Invalid Fast MA"); if(maSlowHandle == INVALID_HANDLE) Print("- Invalid Slow MA"); if(bollingerHandle == INVALID_HANDLE) Print("- Invalid Bollinger Bands"); return INIT_FAILED; } Print("All indicators initialized successfully."); // Verify input parameters if(VolumeThreshold <= 0 || OrderFlowPeriod <= 0 || RiskPercent <= 0 || RiskPercent > 100 || ADXPeriod <= 0 || ADXThreshold <= 0 || MAPeriod <= 0 || MaxLotSize <= 0 || ATRPeriod <= 0 || ATRMultiplier <= 0 || RSIPeriod <= 0 || RSIOverbought <= RSIOversold || MAFastPeriod <= 0 || MASlowPeriod <= 0 || BollingerPeriod <= 0 || BollingerDeviation <= 0 || MaxConsecutiveLosses < 0 || MinBarsBetweenTrades < 0) { Print("Error: Invalid input parameters."); return INIT_FAILED; } Print("Input parameters validated."); // Initialize global variables consecutiveLosses = 0; lastTradeTime = 0; barsSinceLastTrade = MinBarsBetweenTrades;
主要交易逻辑在OnTick()函数中执行,该函数会在每个价格更新时被调用。EA首先检查是否形成了新的K线以及是否允许交易。然后,它通过比较在指定时间段内的买单和卖单成交量来分析订单流。EA使用多种技术指标来确认交易信号,包括趋势强度(ADX)、价格相对于移动平均线的位置以及RSI水平。
void OnTick() { if(!IsNewBar()) return; Print("Current state - Consecutive losses: ", consecutiveLosses, ", Bars since last trade: ", barsSinceLastTrade); if(!IsTradeAllowed()) { Print("Trading not allowed. Check EA configuration and account permissions."); return; } // Check if there's an open position and manage it if(PositionExists()) { ManageOpenPositions(); return; // Exit if there's an open position } barsSinceLastTrade++; // Increment only if there's no open position if(!IsRiskAcceptable()) { Print("Risk not acceptable."); return; } double buyVolume = 0, sellVolume = 0; AnalyzeOrderFlow(buyVolume, sellVolume); double adxValue[], maValue[], atrValue[], rsiValue[], maFastValue[], maSlowValue[], bollingerUpper[], bollingerLower[]; if(!GetIndicatorData(adxValue, maValue, atrValue, rsiValue, maFastValue, maSlowValue, bollingerUpper, bollingerLower)) return; bool strongTrend = (adxValue[0] > ADXThreshold); bool aboveMA = (SymbolInfoDouble(_Symbol, SYMBOL_LAST) > maValue[0]); bool fastAboveSlow = (maFastValue[0] > maSlowValue[0]); int dynamicSL = (int)(atrValue[0] * ATRMultiplier / SymbolInfoDouble(_Symbol, SYMBOL_POINT)); int dynamicTP = dynamicSL * 3; // Risk/Reward ratio of 1:3 double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); // Conditions for a buy trade if(strongTrend && aboveMA && fastAboveSlow && buyVolume > sellVolume + VolumeThreshold && rsiValue[0] < RSIOverbought && currentPrice < bollingerUpper[0] && barsSinceLastTrade >= MinBarsBetweenTrades) { Print("Buy conditions met. Attempting to open position..."); if(ExecuteTrade(ORDER_TYPE_BUY, dynamicSL, dynamicTP)) { Print("Buy position opened successfully."); barsSinceLastTrade = 0; } } // Conditions for a sell trade else if(strongTrend && !aboveMA && !fastAboveSlow && sellVolume > buyVolume + VolumeThreshold && rsiValue[0] > RSIOversold && currentPrice > bollingerLower[0] && barsSinceLastTrade >= MinBarsBetweenTrades) { Print("Sell conditions met. Attempting to open position..."); if(ExecuteTrade(ORDER_TYPE_SELL, dynamicSL, dynamicTP)) { Print("Sell position opened successfully."); barsSinceLastTrade = 0; } } }
为了管理风险,该EA根据账户余额的百分比和当前市场波动性(使用ATR)实施动态仓位调整。它还包含了跟踪止损机制和部分仓位平仓功能,以锁定利润。通过强制设定最大连续亏损次数和交易之间的最小K线数量,EA限制了风险。
bool IsRiskAcceptable() { if(IsHighImpactNews()) { Print("Risk not acceptable: High impact news detected."); return false; } if(consecutiveLosses >= MaxConsecutiveLosses) { Print("Risk not acceptable: Maximum consecutive losses reached (", consecutiveLosses, "/", MaxConsecutiveLosses, ")."); return false; } if(barsSinceLastTrade < MinBarsBetweenTrades) { Print("Risk not acceptable: Not enough bars since last trade (", barsSinceLastTrade, "/", MinBarsBetweenTrades, ")."); return false; } double equity = AccountInfoDouble(ACCOUNT_EQUITY); double balance = AccountInfoDouble(ACCOUNT_BALANCE); double drawdown = (balance - equity) / balance * 100; if(drawdown > 20) // Increased from 10% to 20% { Print("Risk not acceptable: Excessive drawdown (", DoubleToString(drawdown, 2), "%)."); return false; } Print("Risk acceptable. Consecutive losses: ", consecutiveLosses, ", Bars since last trade: ", barsSinceLastTrade, ", Current drawdown: ", DoubleToString(drawdown, 2), "%"); return true; }
CalculateLotSize()函数根据账户余额、风险百分比以及当前市场条件确定适当的仓位大小。ManageOpenPositions()函数处理现有交易,实施跟踪止损和部分平仓。
double CalculateLotSize(double stopLossDistance) { double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE); double maxRiskAmount = accountBalance * (RiskPercent / 100); double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); if(tickValue == 0 || stopLossDistance == 0) { Print("Error: Tick value or Stop Loss distance is 0"); return 0; } double lotSize = NormalizeDouble(maxRiskAmount / (stopLossDistance * tickValue), 2); lotSize = MathFloor(lotSize / lotStep) * lotStep; double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); lotSize = MathMax(MathMin(lotSize, maxLot), minLot); lotSize = MathMin(lotSize, MaxLotSize); double margin = AccountInfoDouble(ACCOUNT_MARGIN_FREE); double requiredMargin = SymbolInfoDouble(_Symbol, SYMBOL_MARGIN_INITIAL) * lotSize; if(requiredMargin > margin) { Print("Not enough free margin to open this position. Required: ", requiredMargin, " Available: ", margin); return 0; } Print("Calculated lot size: ", lotSize, " Risk: $", NormalizeDouble(lotSize * stopLossDistance * tickValue, 2)); return lotSize; }
错误处理通过HandleTradingErrors()函数全面解决,该函数提供关于各种交易相关错误的详细反馈。该EA还包括用于记录交易统计信息的函数,以及用于检查高影响新闻事件的函数(尽管后者留给用户自行实现)。
//+------------------------------------------------------------------+ //| Function for error handling | //+------------------------------------------------------------------+ void HandleTradingErrors(int errorCode) { switch(errorCode) { case TRADE_RETCODE_REQUOTE: Print("Error: Requote"); break; case TRADE_RETCODE_REJECT: Print("Error: Request rejected"); break; case TRADE_RETCODE_CANCEL: Print("Error: Request cancelled by trader"); break; case TRADE_RETCODE_PLACED: Print("Order placed successfully"); break; case TRADE_RETCODE_DONE: Print("Request completed"); break; case TRADE_RETCODE_DONE_PARTIAL: Print("Request partially completed"); break; case TRADE_RETCODE_ERROR: Print("Request processing error"); break; case TRADE_RETCODE_TIMEOUT: Print("Error: Request cancelled by timeout"); break; case TRADE_RETCODE_INVALID: Print("Error: Invalid request"); break; case TRADE_RETCODE_INVALID_VOLUME: Print("Error: Invalid volume in request"); break; case TRADE_RETCODE_INVALID_PRICE: Print("Error: Invalid price in request"); break; case TRADE_RETCODE_INVALID_STOPS: Print("Error: Invalid stops in request"); break; case TRADE_RETCODE_TRADE_DISABLED: Print("Error: Trading is disabled"); break; case TRADE_RETCODE_MARKET_CLOSED: Print("Error: Market is closed"); break; case TRADE_RETCODE_NO_MONEY: Print("Error: Not enough money to complete request"); break; case TRADE_RETCODE_PRICE_CHANGED: Print("Error: Prices changed"); break; case TRADE_RETCODE_PRICE_OFF: Print("Error: No quotes to process request"); break; case TRADE_RETCODE_INVALID_EXPIRATION: Print("Error: Invalid order expiration date"); break; case TRADE_RETCODE_ORDER_CHANGED: Print("Error: Order state changed"); break; case TRADE_RETCODE_TOO_MANY_REQUESTS: Print("Error: Too many requests"); break; case TRADE_RETCODE_NO_CHANGES: Print("Error: No changes in request"); break; case TRADE_RETCODE_SERVER_DISABLES_AT: Print("Error: Autotrading disabled by server"); break; case TRADE_RETCODE_CLIENT_DISABLES_AT: Print("Error: Autotrading disabled by client terminal"); break; case TRADE_RETCODE_LOCKED: Print("Error: Request locked for processing"); break; case TRADE_RETCODE_FROZEN: Print("Error: Order or position frozen"); break; case TRADE_RETCODE_INVALID_FILL: Print("Error: Invalid order filling type"); break; case TRADE_RETCODE_CONNECTION: Print("Error: No connection to trading server"); break; case TRADE_RETCODE_ONLY_REAL: Print("Error: Operation allowed only for live accounts"); break; case TRADE_RETCODE_LIMIT_ORDERS: Print("Error: Pending orders limit reached"); break; case TRADE_RETCODE_LIMIT_VOLUME: Print("Error: Volume limit for orders and positions reached"); break; default: Print("Unknown error: ", errorCode); break; } }
总体而言,这款EA代表了一个复杂的交易系统,它结合了订单流分析、传统技术指标以及高级风险管理技术。它专为经验丰富的交易者设计,在实际部署之前应进行彻底的测试。
注意: 高影响新闻事件的功能尚未完成,这部分我将留给您来完成。
回测
该EA同样适用于5分钟、15分钟和30分钟的时间周期。
您必须对所有时间周期进行充分分析,并优化EA,才能考虑使用它进行交易。
以下是15分钟时间周期的结果
使用15分钟图表的数据,这项回测研究揭示了该交易策略在2000年至2025年期间在欧元兑美元(EURUSD)货币对上的表现。在1:100的杠杆比例和3000美元的初始本金下,账户规模相对较小,而高杠杆可能会放大利润和亏损。在回测期间,该策略在最初的3000美元本金上实现了4.19%的温和回报,总净盈利为125.83美元。
该策略的盈亏比为1.13,即每亏损1美元可获得1.13美元的盈利,表明整体上略微盈利。总共执行了364笔交易,包括167笔多头交易和197笔空头交易。较高的胜率表明交易选择良好,其中空头交易的胜率为73.60%,多头交易的胜率为86.23%。
该策略在盈利交易中获利较小,而在亏损交易中亏损较大,因为盈利交易的平均利润(3.71美元)远低于亏损交易的平均亏损(-12.62美元)。最多连续盈利交易为15笔,最多连续亏损交易为5笔。最大盈利交易为50.22美元,而最大亏损交易为-66.10美元。
该策略在权益曲线上最大回撤为5.63%,这是一个可以接受的亏损水平,表明风险管理较为谨慎。该策略的夏普比率(Sharpe ratio)为1.83,表明其收益足以补偿所承担的风险。
综合来看,这似乎是一种高频剥头皮策略,旨在通过大量小额盈利交易获利,但偶尔也会遭受较大亏损。其高胜率和较低的盈亏比表明,如果市场条件发生变化,该策略可能容易遭受重大亏损。请再次检查您的答案。
5分钟时间框架
使用5分钟图表的数据,这项回测研究分析了该交易策略在2000年1月1日至2025年2月1日期间在欧元兑美元(EURUSD)货币对上的表现。该策略使用3000美元的初始本金和1:100的杠杆比例,表明账户规模较小且杠杆较高,可能会放大收益和亏损。
根据回测结果,最初的3000美元本金在25年期间实现了150.32美元的总净盈利,相当于温和的5.01%的回报率。该策略共执行了1732笔交易,包括1023笔空头交易和709笔多头交易。从高胜率来看,无论是空头还是多头交易,交易选择都表现良好,空头交易的胜率为81.82%,多头交易的胜率为81.95%。
该策略在盈利交易中获利较小,但在亏损交易中亏损较大,因为盈利交易的平均利润(2.27美元)远低于亏损交易的平均亏损(-9.79美元)。盈亏比为1.05,即每亏损1美元可获得1.05美元的盈利,表明该策略整体上仅略微盈利。该策略在权益曲线上最大回撤为9.95%,这是一个合理的水平,但可能会引发对风险管理的质疑。
该策略的夏普比率(Sharpe ratio)为0.92,表明其收益仅勉强补偿了所承担的风险。权益曲线总体呈现上升趋势,但存在显著的波动和下行阶段。
该策略参数建议使用复杂的多因素方法进行交易决策,结合多种技术指标,如ADX、RSI、移动平均线和布林带。综合来看,这似乎是一种高频剥头皮策略,旨在通过大量小额盈利交易获利,但偶尔也会遭受较大亏损。鉴于其高胜率但低盈利因子,该策略可能难以在长期内提供可观的回报,并且在不利的市场条件下容易出现重大回撤。
结论
这款MetaTrader 5的EA采用复杂的订单流剥头皮策略,并结合了先进的风险管理工具。它通过结合多种技术指标、订单流分析和动态仓位调整来寻找高概率的外汇交易机会。在不同时间周期(尤其是15分钟和5分钟)对EURUSD货币对进行回测,显示出该EA的潜力。
然而,结果也显示了该策略的优缺点。尽管该策略的胜率较高且盈利温和,但由于其低盈利因子和在长时间测试期间的较小收益,可能无法产生较大的回报。由于该策略倾向于通过频繁的小额盈利来抵消偶尔出现的较大亏损,因此在不利的市场条件下容易出现重大回撤。
*请将mq5文件保存在MQL5/Expert Advisors/文件夹中(或其内部的某个位置)。
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/15895

