//+------------------------------------------------------------------+ //| Pinbar_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 = 123457; input string OrderComment = "Pinbar_Strategy"; input group "=====交易参数=====" input double StartLots = 0.1; // 开仓手数 input bool EnableDynamicLots = true; // 是否启用动态手数(按余额比例) input double BaseBalance = 10000.0; // 基准余额(用于动态手数计算) input group "=====Pinbar识别参数=====" input double PinbarBodyRatio = 0.3; // Pinbar实体比例(实体/总长度) input double PinbarShadowRatio = 0.6; // Pinbar影线比例(影线/总长度) input int MinPinbarLength = 10; // 最小Pinbar长度(点数) input group "=====交易方向开关=====" input bool EnableBuy = true; // 是否允许做多 input bool EnableSell = true; // 是否允许做空 input group "=====ATR控制=====" input bool EnableATRFilter = true; // 是否启用ATR过滤 input int ATRPeriod = 14; // ATR周期 input double ATRThreshold = 0.0003; // ATR阈值,大于此值才允许开仓 input group "=====交易量过滤=====" input bool EnableVolumeFilter = true; // 是否启用交易量过滤 input double VolumeIncreaseRatio = 0.2; // 交易量增加比例(20% = 0.2) //+------------------------------------------------------------------+ //| 全局变量 | //+------------------------------------------------------------------+ datetime lastPinbarTime = 0; // 上次识别到Pinbar的时间 bool isPinbarProcessed = false; // 当前Pinbar是否已处理 // ATR指标句柄 int atrHandle = INVALID_HANDLE; //+------------------------------------------------------------------+ //| Pinbar结构体 | //+------------------------------------------------------------------+ struct PinbarInfo { bool isValid; // 是否为有效Pinbar bool isBullish; // 是否为看涨Pinbar double open; // 开盘价 double high; // 最高价 double low; // 最低价 double close; // 收盘价 double bodySize; // 实体大小 double upperShadow; // 上影线 double lowerShadow; // 下影线 double totalLength; // 总长度 datetime time; // K线时间 }; //+------------------------------------------------------------------+ //| 获取K线数据 | //+------------------------------------------------------------------+ bool GetCandleData(int shift, double &open, double &high, double &low, double &close, datetime &time) { double o[1], h[1], l[1], c[1]; datetime t[1]; if(CopyOpen(_Symbol, _Period, shift, 1, o) != 1) return false; if(CopyHigh(_Symbol, _Period, shift, 1, h) != 1) return false; if(CopyLow(_Symbol, _Period, shift, 1, l) != 1) return false; if(CopyClose(_Symbol, _Period, shift, 1, c) != 1) return false; if(CopyTime(_Symbol, _Period, shift, 1, t) != 1) return false; open = o[0]; high = h[0]; low = l[0]; close = c[0]; time = t[0]; return true; } //+------------------------------------------------------------------+ //| 识别Pinbar形态 | //+------------------------------------------------------------------+ PinbarInfo IdentifyPinbar(int shift = 1) { PinbarInfo pinbar; pinbar.isValid = false; double open, high, low, close; datetime time; if(!GetCandleData(shift, open, high, low, close, time)) { return pinbar; } // 计算K线各部分 pinbar.open = open; pinbar.high = high; pinbar.low = low; pinbar.close = close; pinbar.time = time; pinbar.bodySize = MathAbs(close - open); pinbar.totalLength = high - low; if(open > close) { // 阴线 pinbar.upperShadow = high - open; pinbar.lowerShadow = close - low; } else { // 阳线 pinbar.upperShadow = high - close; pinbar.lowerShadow = open - low; } // 检查是否为有效Pinbar if(pinbar.totalLength <= 0) return pinbar; // 检查最小长度 double minLength = MinPinbarLength * SymbolInfoDouble(_Symbol, SYMBOL_POINT); if(pinbar.totalLength < minLength) return pinbar; // 检查实体比例 double bodyRatio = pinbar.bodySize / pinbar.totalLength; if(bodyRatio > PinbarBodyRatio) return pinbar; // 检查影线比例 double shadowRatio = MathMax(pinbar.upperShadow, pinbar.lowerShadow) / pinbar.totalLength; if(shadowRatio < PinbarShadowRatio) return pinbar; // 判断Pinbar方向 if(pinbar.lowerShadow > pinbar.upperShadow) { // 下影线长,看涨Pinbar pinbar.isBullish = true; } else if(pinbar.upperShadow > pinbar.lowerShadow) { // 上影线长,看空Pinbar pinbar.isBullish = false; } else { // 影线相等,不是有效Pinbar return pinbar; } pinbar.isValid = true; return pinbar; } //+------------------------------------------------------------------+ //| 检查是否有指定方向的持仓 | //+------------------------------------------------------------------+ bool HasPosition(int posType) { for(int i=0; i= 阈值(", VolumeIncreaseRatio * 100, "%)"); return true; } //+------------------------------------------------------------------+ //| 计算开仓手数:可选动态手数(按余额比例)或固定手数 | //+------------------------------------------------------------------+ double GetDynamicLots() { // 规范化工具 double minLots = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double maxLots = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); double stepLots = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); if(!EnableDynamicLots) { // 固定手数模式:直接使用 StartLots,并按交易品种步进规范化 double fixedLots = StartLots; fixedLots = MathMax(minLots, MathMin(maxLots, fixedLots)); fixedLots = NormalizeDouble(fixedLots / stepLots, 0) * stepLots; return fixedLots; } // 动态手数模式:当前余额 / 基准余额 * 基准手数 double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE); double baseLots = StartLots; // 基准手数 double dynamicLots = (accountBalance / BaseBalance) * baseLots; // 规范化手数 dynamicLots = MathMax(minLots, MathMin(maxLots, dynamicLots)); dynamicLots = NormalizeDouble(dynamicLots / stepLots, 0) * stepLots; return dynamicLots; } //+------------------------------------------------------------------+ //| 开仓函数 | //+------------------------------------------------------------------+ void OpenPosition(int posType, double lots, double price, double sl, double tp, string comment) { trade.SetExpertMagicNumber(MagicNumber); bool result = false; if(posType == POSITION_TYPE_BUY) { result = trade.Buy(lots, _Symbol, price, sl, tp, comment); } else if(posType == POSITION_TYPE_SELL) { result = trade.Sell(lots, _Symbol, price, sl, tp, comment); } if(result) { string direction = (posType == POSITION_TYPE_BUY) ? "多单" : "空单"; Print("开仓成功:价格:", price, " 手数:", lots, " 止损:", sl, " 止盈:", tp); } else { Print("开仓失败: 错误代码:", trade.ResultRetcode(), " 错误描述:", trade.ResultRetcodeDescription()); } } //+------------------------------------------------------------------+ //| 计算止盈止损价格 | //+------------------------------------------------------------------+ void CalculateStopLossAndTakeProfit(PinbarInfo &pinbar, double &sl, double &tp) { double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); if(pinbar.isBullish) { // 看涨Pinbar:止损在Pinbar最低点,止盈为开仓点向上Pinbar整体长度的距离 sl = pinbar.low; tp = pinbar.close + pinbar.totalLength; } else { // 看空Pinbar:止损在Pinbar最高点,止盈为开仓点向下Pinbar整体长度的距离 sl = pinbar.high; tp = pinbar.close - pinbar.totalLength; } // 规范化价格 sl = NormalizeDouble(sl, _Digits); tp = NormalizeDouble(tp, _Digits); } //+------------------------------------------------------------------+ //| 处理Pinbar信号 | //+------------------------------------------------------------------+ void ProcessPinbarSignal(PinbarInfo &pinbar) { // 检查是否已处理过这个Pinbar if(pinbar.time == lastPinbarTime && isPinbarProcessed) { return; } // 检查ATR过滤条件 if(!CheckATRFilter()) { return; } // 检查交易量过滤条件 if(!CheckVolumeFilter()) { return; } // 移除持仓数量限制,允许同时持有多空仓位 // 确定交易方向 int posType = -1; if(pinbar.isBullish && EnableBuy) { posType = POSITION_TYPE_BUY; } else if(!pinbar.isBullish && EnableSell) { posType = POSITION_TYPE_SELL; } if(posType == -1) { Print("交易方向被禁用,跳过Pinbar信号"); return; } // 计算开仓手数 double lots = GetDynamicLots(); // 计算止盈止损 double sl, tp; CalculateStopLossAndTakeProfit(pinbar, sl, tp); // 获取开仓价格 double price = (posType == POSITION_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID); // 开仓 string direction = (posType == POSITION_TYPE_BUY) ? "看涨" : "看空"; string comment = OrderComment + "_" + direction + "Pinbar"; OpenPosition(posType, lots, price, sl, tp, comment); // 更新处理状态 lastPinbarTime = pinbar.time; isPinbarProcessed = true; Print("处理", direction, "Pinbar信号完成 - 时间:", TimeToString(pinbar.time), " 开仓价:", price, " 止损:", sl, " 止盈:", tp); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // 识别上根K线的Pinbar形态 PinbarInfo pinbar = IdentifyPinbar(1); if(pinbar.isValid) { ProcessPinbarSignal(pinbar); } // 重置处理状态(当新K线开始时) static datetime lastBarTime = 0; datetime currentBarTime = iTime(_Symbol, _Period, 0); if(currentBarTime != lastBarTime) { isPinbarProcessed = false; lastBarTime = currentBarTime; } } //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { Print("=== Pinbar策略EA初始化 ==="); Print("Pinbar识别参数 - 实体比例:", PinbarBodyRatio, " 影线比例:", PinbarShadowRatio, " 最小长度:", MinPinbarLength); Print("交易方向 - 多单:", (EnableBuy ? "启用" : "禁用"), " 空单:", (EnableSell ? "启用" : "禁用")); Print("手数模式:", (EnableDynamicLots ? "动态手数" : "固定手数")); if(EnableDynamicLots) { Print("基准余额:", BaseBalance, " 基准手数:", StartLots); } else { Print("固定手数:", StartLots); } Print("ATR过滤:", (EnableATRFilter ? "启用" : "禁用")); if(EnableATRFilter) { Print("ATR周期:", ATRPeriod, " ATR阈值:", ATRThreshold); } Print("交易量过滤:", (EnableVolumeFilter ? "启用" : "禁用")); if(EnableVolumeFilter) { Print("交易量增加比例阈值:", VolumeIncreaseRatio * 100, "%"); } Print("当前服务器时间: ", TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES)); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(atrHandle != INVALID_HANDLE) IndicatorRelease(atrHandle); Print("Pinbar策略EA已停止,原因代码:", reason); }