//+------------------------------------------------------------------+ //| RSI_Strategy.mq5 | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include CTrade trade; //+------------------------------------------------------------------+ //| 输入参数 | //+------------------------------------------------------------------+ input group "=====基本参数======" input int MagicNumber = 123456; input string OrderComment = "RSI_Strategy"; input group "=====马丁参数=====" input double MartinStartLots = 0.1; // 首次进场手数(基准:10000美元=0.1手) input double MartinStepUSD = 3; // 马丁加仓间隔(美金,作为ATR不触发时的回退值) input double MartinMultiplier = 1.3; // 马丁加仓倍数 input double TakeProfitUSD = 3; // 盈利出场(美金) input int PriceDigits = 2; // 价格小数位(如XAUUSD有的平台是2,有的是3) input int MaxAddCount = 15; // 最大加仓次数 input bool EnableDynamicLots = true; // 是否启用动态手数(按余额比例),关闭则固定用MartinStartLots input group "=====动态加仓参数=====" input int ATRPeriod = 2; // ATR周期 input double ATRThresholdUSD = 0.6; // ATR阈值(美金) input double ATRDistanceMultiplier = 1;// ATR加仓距离系数 input group "=====EMA加仓风控=====" input bool EnableEMARisk = false; // 开关:当加仓次数超过阈值后启用EMA风控 input int EMARiskStartAddCount = 10; // 超过该加仓次数后启用风控 input int EMARiskPeriod = 50; // EMA周期(默认50) input group "=====指标参数=====" input double RSIUpperLevel = 85; // RSI超买水平 input double RSILowerLevel = 25; // RSI超卖水平 input bool EnableRSIFilter = false; // 是否启用RSI过滤 input double ADXThreshold = 50; // ADX阈值,大于此值时暂停加仓 input bool EnableADXFilter = true; // 是否启用ADX过滤功能 input group "=====交易方向开关=====" input bool EnableBuy = true; // 是否允许做多 input bool EnableSell = true; // 是否允许做空 input group "=====时间段控制=====" input bool EnableTimeControl = false; // 是否时间段控制 input string TradingHours = "1,2,3,4,5,6,7,8,9,10"; // 允许开仓的时间段(小时),用逗号分割 // RSI指标句柄 int rsiHandle = INVALID_HANDLE; // ADX指标句柄 int adxHandle = INVALID_HANDLE; // ATR指标句柄 int atrHandle = INVALID_HANDLE; // EMA12指标句柄 int ema12Handle = INVALID_HANDLE; // EMA风控指标句柄 int emaRiskHandle = INVALID_HANDLE; // 记录上一次加仓价和当前加仓手数 static double lastLongAddPrice = 0; static double lastLongAddLots = 0; // 记录上一次空单加仓价和手数 static double lastShortAddPrice = 0; static double lastShortAddLots = 0; // 记录加仓次数 static int buyAddCount = 0; static int sellAddCount = 0; // 记录移动追踪止盈的历史最高/最低价 static double buyTrailingPrice = 0; // 多单移动追踪止盈位 static double sellTrailingPrice = 0; // 空单移动追踪止盈位 // 获取当前RSI值 double GetRSI() { if(rsiHandle == INVALID_HANDLE) rsiHandle = iRSI(_Symbol, _Period, 14, PRICE_CLOSE); // 获取当前RSI值 double rsiArray[1]; if(CopyBuffer(rsiHandle, 0, 0, 1, rsiArray) <= 0) return 0.0; return rsiArray[0]; // 获取上一根K线的RSI值 // double rsiArray[2]; // if(CopyBuffer(rsiHandle, 0, 1, 2, rsiArray) <= 0) return 0.0; // return rsiArray[0]; } // 获取基于ATR的动态加仓距离(单位:美金)。当ATR>阈值时使用 ATR*系数,否则使用固定MartinStepUSD double CalculateDynamicAddDistanceUSD() { if(atrHandle == INVALID_HANDLE) atrHandle = iATR(_Symbol, _Period, ATRPeriod); if(atrHandle != INVALID_HANDLE) { double atrArray[1]; if(CopyBuffer(atrHandle, 0, 0, 1, atrArray) == 1) { double atrNow = atrArray[0]; if(atrNow > ATRThresholdUSD) { double distance = atrNow * ATRDistanceMultiplier; return NormalizeDouble(distance, 2); } } } return NormalizeDouble(MartinStepUSD, 2); } // 读取上一根K线的开收盘 bool GetPrevCandle(double &prevOpen, double &prevClose) { double o[1], c[1]; if(CopyOpen(_Symbol, _Period, 1, 1, o) != 1) return false; if(CopyClose(_Symbol, _Period, 1, 1, c) != 1) return false; prevOpen = o[0]; prevClose = c[0]; return true; } // 获取指定周期EMA在上一根K线的值 bool GetPrevEMA(int period, double &emaPrev) { if(emaRiskHandle == INVALID_HANDLE) emaRiskHandle = iMA(_Symbol, _Period, period, 0, MODE_EMA, PRICE_CLOSE); if(emaRiskHandle == INVALID_HANDLE) return false; double emaArr[1]; if(CopyBuffer(emaRiskHandle, 0, 1, 1, emaArr) != 1) return false; emaPrev = emaArr[0]; return true; } // EMA风控: // - 多单:上一根K线实体上穿EMA(上一根开盘EMA) // - 空单:上一根K线实体收在EMA下方(上一根收盘 emaPrev); } else { return (prevClose < emaPrev); } } // 获取当前ADX值 double GetADX() { if(adxHandle == INVALID_HANDLE) adxHandle = iADX(_Symbol, _Period, 14); // 获取当前ADX值 double adxArray[1]; if(CopyBuffer(adxHandle, 0, 0, 1, adxArray) <= 0) return 0.0; return adxArray[0]; } // 获取上根K线收盘价 double GetPreviousClose() { double closeArray[2]; if(CopyClose(_Symbol, _Period, 1, 2, closeArray) <= 0) return 0.0; return closeArray[1]; // 上根K线收盘价 } // 获取当前EMA12值 double GetEMA12() { if(ema12Handle == INVALID_HANDLE) ema12Handle = iMA(_Symbol, _Period, 12, 0, MODE_EMA, PRICE_CLOSE); double emaArray[2]; if(CopyBuffer(ema12Handle, 0, 0, 2, emaArray) <= 0) return 0.0; return emaArray[1]; } //+------------------------------------------------------------------+ //| 检查当前时间是否在允许开仓的时间段内 | //+------------------------------------------------------------------+ bool IsTradingTime() { if(!EnableTimeControl) return true; // 获取当前服务器时间 MqlDateTime dt; TimeToStruct(TimeCurrent(), dt); int currentHour = dt.hour; // 解析允许的时间段 string hours[]; StringSplit(TradingHours, ',', hours); for(int i = 0; i < ArraySize(hours); i++) { // 去除空格并转换为整数 string hourStr = hours[i]; // 去除前后空格 while(StringLen(hourStr) > 0 && StringGetCharacter(hourStr, 0) == ' ') { hourStr = StringSubstr(hourStr, 1); } while(StringLen(hourStr) > 0 && StringGetCharacter(hourStr, StringLen(hourStr)-1) == ' ') { hourStr = StringSubstr(hourStr, 0, StringLen(hourStr)-1); } if(hourStr != "") { int hour = (int)StringToInteger(hourStr); if(hour == currentHour) { return true; // 当前时间在允许的时间段内 } } } return false; // 当前时间不在允许的时间段内 } //+------------------------------------------------------------------+ //| 检查是否有指定方向的持仓 | //+------------------------------------------------------------------+ bool HasPosition(int posType) { for(int i=0; i=0; i--) { ulong ticket = PositionGetTicket(i); if(PositionSelectByTicket(ticket)) { if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetInteger(POSITION_TYPE) == posType) { if(trade.PositionClose(ticket)) { string direction = (posType == POSITION_TYPE_BUY) ? "多单" : "空单"; } } } } } //+------------------------------------------------------------------+ //| 计算指定方向持仓的加权平均价 | //+------------------------------------------------------------------+ double GetAvgPrice(int posType) { double totalLots = 0; double totalCost = 0; for(int i=0; i= MaxAddCount) { string direction = (posType == POSITION_TYPE_BUY) ? "多单" : "空单"; Print(direction, "已达到最大加仓次数(", currentAddCount, " >= ", MaxAddCount, "),停止加仓"); return false; } bool shouldAdd = false; if(posType == POSITION_TYPE_BUY) { shouldAdd = (curPrice <= lastAddPrice - stepDistance * SymbolInfoDouble(_Symbol, SYMBOL_POINT)); } else { shouldAdd = (curPrice >= lastAddPrice + stepDistance * SymbolInfoDouble(_Symbol, SYMBOL_POINT)); } if(!shouldAdd) return false; // EMA风控:当开启且加仓次数达到阈值后,要求满足指定的K线与EMA关系 if(EnableEMARisk && currentAddCount >= EMARiskStartAddCount) { if(!PassesEMARisk(posType)) { string direction2 = (posType == POSITION_TYPE_BUY) ? "多单" : "空单"; Print(direction2, " EMA风控未通过(AddCount=", currentAddCount, ", 阈值=", EMARiskStartAddCount, ", EMA", EMARiskPeriod, "),暂停加仓"); return false; } } // 检查ADX条件(趋势强度) if(EnableADXFilter) { double adx = GetADX(); if(adx > ADXThreshold) { string direction = (posType == POSITION_TYPE_BUY) ? "多单" : "空单"; Print(direction, "ADX值过高(", adx, " > ", ADXThreshold, "),暂停加仓"); return false; } } return true; } //+------------------------------------------------------------------+ //| 写回全局变量 | //+------------------------------------------------------------------+ void UpdateGlobalVariables(int posType, double lastAddPrice, double lastAddLots, int currentAddCount) { if(posType == POSITION_TYPE_BUY) { lastLongAddPrice = lastAddPrice; lastLongAddLots = lastAddLots; buyAddCount = currentAddCount; } else { lastShortAddPrice = lastAddPrice; lastShortAddLots = lastAddLots; sellAddCount = currentAddCount; } } //+------------------------------------------------------------------+ //| 检查80%移动追踪止盈条件,达到后全部平仓 | //+------------------------------------------------------------------+ void CheckTrailingTakeProfit(int posType, double currentPrice, double avgPrice) { // 检查盈亏平衡价是否有效 if(avgPrice == 0) { return; } // 计算80%移动追踪止盈价格 double trailingPrice = 0; if(posType == POSITION_TYPE_BUY) { // 多单:当前价格 - (当前价格 - 盈亏平衡价) × 80% double profitDistance = currentPrice - avgPrice; if(profitDistance <= 0) return; // 没有利润,不检查 trailingPrice = currentPrice - (profitDistance * 0.3); // 更新多单移动追踪止盈位(取最高值) if(trailingPrice > buyTrailingPrice || buyTrailingPrice == 0) { buyTrailingPrice = trailingPrice; Print("多单移动追踪止盈位更新:", buyTrailingPrice, " 当前价:", currentPrice, " 盈亏平衡价:", avgPrice); } // 使用历史最高止盈位进行检查 trailingPrice = buyTrailingPrice; } else { // 空单:当前价格 + (盈亏平衡价 - 当前价格) × 80% double profitDistance = avgPrice - currentPrice; if(profitDistance <= 0) return; // 没有利润,不检查 trailingPrice = currentPrice + (profitDistance * 0.3); // 更新空单移动追踪止盈位(取最低值) if(trailingPrice < sellTrailingPrice || sellTrailingPrice == 0) { sellTrailingPrice = trailingPrice; Print("空单移动追踪止盈位更新:", sellTrailingPrice, " 当前价:", currentPrice, " 盈亏平衡价:", avgPrice); } // 使用历史最低止盈位进行检查 trailingPrice = sellTrailingPrice; } // 规范化价格 trailingPrice = NormalizeDouble(trailingPrice, _Digits); // 检查是否达到移动追踪止盈条件 bool shouldClose = false; if(posType == POSITION_TYPE_BUY) { // 多单:当前价格回撤到移动止盈位以下 shouldClose = (currentPrice <= trailingPrice); } else { // 空单:当前价格反弹到移动止盈位以上 shouldClose = (currentPrice >= trailingPrice); } if(shouldClose) { Print("达到80%移动追踪止盈条件,", (posType == POSITION_TYPE_BUY) ? "多单" : "空单", " 当前价:", currentPrice, " 移动止盈位:", trailingPrice); // 调用现有的平仓函数 ClosePosition(posType); Print("80%移动追踪止盈平仓完成"); // 重置移动追踪止盈位 if(posType == POSITION_TYPE_BUY) { buyTrailingPrice = 0; } else { sellTrailingPrice = 0; } } } //+------------------------------------------------------------------+ //| 入场条件检查函数 | //+------------------------------------------------------------------+ bool CheckEntryFirst(int posType, double rsi, double rsiThreshold, double totalLots) { // 1. 检查是否已有持仓 if(totalLots > 0) return false; // 2. 检查时间段是否允许开仓 if(!IsTradingTime()) { Print("当前时间不在允许开仓的时间段内,禁止开仓"); return false; } // 3. 检查RSI过滤条件 bool shouldEnter = false; if(EnableRSIFilter) { shouldEnter = (posType == POSITION_TYPE_BUY) ? (rsi < rsiThreshold) : (rsi > rsiThreshold); } else { shouldEnter = true; // 不用RSI过滤时,只要没持仓就允许开仓 } if(!shouldEnter) return false; return true; } //+------------------------------------------------------------------+ //| 计算开仓手数:可选动态手数(按余额比例)或固定手数 | //+------------------------------------------------------------------+ double GetDynamicLots(int posType) { // 规范化工具 double minLots = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double maxLots = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); double stepLots = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); if(!EnableDynamicLots) { // 固定手数模式:直接使用 MartinStartLots,并按交易品种步进规范化 double fixedLots = MartinStartLots; fixedLots = MathMax(minLots, MathMin(maxLots, fixedLots)); fixedLots = NormalizeDouble(fixedLots / stepLots, 0) * stepLots; return fixedLots; } // 动态手数模式:当前余额 / 基准余额 * 基准手数 double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE); double baseBalance = 10000.0; // 基准余额 double baseLots = MartinStartLots; // 基准手数 double dynamicLots = (accountBalance / baseBalance) * baseLots; // 规范化手数 dynamicLots = MathMax(minLots, MathMin(maxLots, dynamicLots)); dynamicLots = NormalizeDouble(dynamicLots / stepLots, 0) * stepLots; return dynamicLots; } //+------------------------------------------------------------------+ //| 马丁策略主逻辑 | //+------------------------------------------------------------------+ void MartinLogic(int posType, double rsi, double rsiThreshold) { double curPrice = (posType == POSITION_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID); double avgPrice = GetAvgPrice(posType); double totalLots = GetTotalLots(posType); double lastAddPrice = (posType == POSITION_TYPE_BUY) ? lastLongAddPrice : lastShortAddPrice; double lastAddLots = (posType == POSITION_TYPE_BUY) ? lastLongAddLots : lastShortAddLots; int currentAddCount = (posType == POSITION_TYPE_BUY) ? buyAddCount : sellAddCount; string direction = (posType == POSITION_TYPE_BUY) ? "多单" : "空单"; // 计算实际点数(美金转点数),加仓距离采用动态计算 int multiplier = (PriceDigits == 2) ? 100 : ((PriceDigits == 3) ? 1000 : 1); double stepDistanceUSD = CalculateDynamicAddDistanceUSD(); double stepDistance = stepDistanceUSD * multiplier; double tpDistance = TakeProfitUSD * multiplier; // 1. 首次进场 bool shouldEnter = CheckEntryFirst(posType, rsi, rsiThreshold, totalLots); if(shouldEnter) { ExecuteOpenPosition(posType, rsi, rsiThreshold, curPrice); return; } // 2. 盈利出场 bool shouldTakeProfit = false; if(posType == POSITION_TYPE_BUY) { shouldTakeProfit = (totalLots > 0 && curPrice >= avgPrice + tpDistance * SymbolInfoDouble(_Symbol, SYMBOL_POINT)); } else { shouldTakeProfit = (totalLots > 0 && curPrice <= avgPrice - tpDistance * SymbolInfoDouble(_Symbol, SYMBOL_POINT)); } if(shouldTakeProfit) { ClosePosition(posType); lastAddPrice = 0; lastAddLots = 0; currentAddCount = 0; // 为所有同方向持仓设置80%追踪止盈TP // CheckTrailingTakeProfit(posType, curPrice, avgPrice); // 写回全局变量 UpdateGlobalVariables(posType, lastAddPrice, lastAddLots, currentAddCount); // 平仓后立即检查是否可以开新的首仓 bool shouldEnter = CheckEntryFirst(posType, rsi, rsiThreshold, 0); // 传入0因为刚平仓,没有持仓 if(shouldEnter) { ExecuteOpenPosition(posType, rsi, rsiThreshold, curPrice); } return; } // 4. 马丁加仓 bool shouldAdd = CheckAddPosition(posType, curPrice, lastAddPrice, stepDistance, totalLots, currentAddCount); if(shouldAdd) { double nextLots = NormalizeDouble(lastAddLots * MartinMultiplier, 2); OpenPosition(posType, nextLots, curPrice, OrderComment); lastAddPrice = curPrice; lastAddLots = nextLots; currentAddCount++; // 增加加仓次数 Print("马丁加仓", direction, ",价格:", curPrice, " 手数:", nextLots, " 加仓次数:", currentAddCount, "/", MaxAddCount); UpdateGlobalVariables(posType, lastAddPrice, lastAddLots, currentAddCount); return; } } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { double rsi = GetRSI(); // 马丁策略逻辑 if(EnableBuy) MartinLogic(POSITION_TYPE_BUY, rsi, RSILowerLevel); // 多单马丁 if(EnableSell) MartinLogic(POSITION_TYPE_SELL, rsi, RSIUpperLevel); // 空单马丁 } //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // 打印允许开仓的时间段 Print("=== RSI策略EA初始化 ==="); Print("允许开仓的时间段: ", TradingHours); Print("最大加仓次数: ", MaxAddCount); Print("ADX过滤功能: ", (EnableADXFilter ? "启用" : "禁用")); if(EnableADXFilter) { Print("ADX过滤阈值: ", ADXThreshold); } Print("当前服务器时间: ", TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES)); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(rsiHandle != INVALID_HANDLE) IndicatorRelease(rsiHandle); if(adxHandle != INVALID_HANDLE) IndicatorRelease(adxHandle); if(ema12Handle != INVALID_HANDLE) IndicatorRelease(ema12Handle); if(atrHandle != INVALID_HANDLE) IndicatorRelease(atrHandle); if(emaRiskHandle != INVALID_HANDLE) IndicatorRelease(emaRiskHandle); }