【黄金小霸王】金字塔滚仓

| 发表于 13 小时前 | 显示全部楼层 |复制链接
//+------------------------------------------------------------------+
//|                                          DualMA_MACD_Filter.mq5 |
//|                                    Generated by OpenAI            |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "OpenAI"
#property link      ""
#property version   "1.95"

#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>

//--- 输入参数
input group "=== 均线设置 ==="
input int      InpFastMA          = 1;            // 快线周期(K线根数)
input ENUM_TIMEFRAMES InpFastMATimeframe = PERIOD_CURRENT; // 快线图表周期
input int      InpSlowMA          = 72;           // 慢线周期(K线根数)
input ENUM_TIMEFRAMES InpSlowMATimeframe = PERIOD_CURRENT; // 慢线图表周期
input int      InpFilterMA        = 144;          // 过滤均线周期(K线根数)
input ENUM_TIMEFRAMES InpFilterMATimeframe = PERIOD_CURRENT; // 过滤线图表周期
input ENUM_MA_METHOD InpMAMethod  = MODE_SMA;     // 均线类型
input bool     InpOpenOnCross     = true;         // 仅在均线交叉时开仓(true=交叉开仓,false=持续满足即开仓)

input group "=== MACD 设置 ==="
input int      InpMACDFast        = 6;            // MACD 快线
input int      InpMACDSlow        = 13;           // MACD 慢线
input int      InpMACDSignal      = 5;            // MACD 信号线
input bool     InpUseMACDValidBars = false;       // 启用能量柱有效根数限制(false=仅要求能量柱方向)
input int      InpMACDValidBars   = 2;            // MACD能量柱有效根数(穿越零轴后允许开仓的K线数)

input group "=== 资金管理(复利手数) ==="
input double   InpMoneyPerLot     = 1000.0;       // 每多少资金对应基础手数
input double   InpBaseLot         = 0.01;         // 基础手数(每 InpMoneyPerLot 资金对应的手数)
input double   InpLotIncrement    = 0.01;         // 亏损递增步长(固定增加)
input double   InpMaxLots         = 1.0;          // 最大手数上限(达到后不再增加,但继续交易)
input double   InpDailyProfitLimit= 500.0;        // 每日盈利限制(美元,达到后暂停开仓)

input group "=== 出场设置 ==="
input bool     InpUseFixedSLTP    = true;         // 启用固定止盈止损
input int      InpTakeProfit      = 0;            // 止盈点数(0=禁用)
input int      InpStopLoss        = 0;            // 止损点数(0=禁用)
input bool     InpUseReverse      = true;         // 启用均线反转反手
input bool     InpUseFilterClose  = true;         // 启用过滤均线平仓(收盘价穿越过滤线时平仓)

input group "=== 移动止损 ==="
input bool     InpUseTrailingStop = false;        // 启用移动止损
input int      InpTrailingStart   = 1000;         // 触发移动止损的盈利点数
input int      InpTrailingStep    = 1000;         // 移动止损距离点数

input group "=== 交易时间限制(服务器时间) ==="
input string   InpStartTime       = "00:00";      // 允许开首单的开始时间 (HH:MM)
input string   InpStopTime        = "23:59";      // 允许开首单的暂停时间 (HH:MM)

input group "=== 其他 ==="
input long     InpMagicNumber     = 16887;        // 魔术号
input bool     InpUseFilter       = true;         // 启用过滤均线(开仓方向过滤)

//--- 全局变量
CTrade          trade;
CPositionInfo   posInfo;
double          g_currentLot = 0.0;               // 当前应使用的开仓手数(动态计算)
double          g_dailyProfit = 0.0;              // 当日已实现盈利(美元)
datetime        g_lastBarTime = 0;                // 上一根K线时间
int             g_lastDay = -1;                   // 上次记录的日号(用于重置每日盈利)

//--- 移动止损专用
double          g_trailingStopPrice = 0.0;        // 当前移动止损价位
double          g_extremePrice = 0.0;             // 持仓期间最高(多单)或最低(空单)价
bool            g_trailingTriggered = false;      // 是否已触发移动止损

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   // 检查 MACD 有效根数
   if(InpUseMACDValidBars && InpMACDValidBars < 1)
   {
      Print("错误:MACD有效根数必须>=1");
      return(INIT_PARAMETERS_INCORRECT);
   }

   // 检查时间格式
   if(!IsValidTimeFormat(InpStartTime) || !IsValidTimeFormat(InpStopTime))
   {
      Print("错误:时间格式必须为 HH:MM");
      return(INIT_PARAMETERS_INCORRECT);
   }

   // 检查资金管理参数
   if(InpMoneyPerLot <= 0 || InpBaseLot <= 0)
   {
      Print("错误:每手对应资金和基础手数必须大于0");
      return(INIT_PARAMETERS_INCORRECT);
   }

   // 初始化当前手数为动态计算的首单手数
   g_currentLot = CalculateDynamicLot();
   g_dailyProfit = 0.0;
   g_lastBarTime = 0;
   g_lastDay = -1;

   // 重置移动止损变量
   g_trailingStopPrice = 0.0;
   g_extremePrice = 0.0;
   g_trailingTriggered = false;

   trade.SetExpertMagicNumber(InpMagicNumber);
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   //--- 检查是否新的一天(服务器时间),重置当日盈利
   MqlDateTime dt;
   TimeToStruct(TimeCurrent(), dt);
   if(dt.day != g_lastDay)
   {
      if(g_lastDay != -1)
         g_dailyProfit = 0.0;
      g_lastDay = dt.day;
   }

   //--- 1. 实时检查固定止盈止损(每个tick)
   if(InpUseFixedSLTP)
      CheckFixedSLTP();

   //--- 2. 实时检查移动止损(每个tick)
   CheckTrailingStop();

   //--- 3. 检测新K线
   datetime currentBarTime = iTime(_Symbol, _Period, 0);
   bool isNewBar = (currentBarTime != g_lastBarTime);
   if(!isNewBar)
      return;
   g_lastBarTime = currentBarTime;

   //--- 4. 获取指标数据(基于各自周期的前一根K线,即shift=1)
   double fastMA[1], slowMA[1], filterMA[1];
   double macdHist[];
   int macdBarsNeeded = (InpUseMACDValidBars) ? InpMACDValidBars + 1 : 1;
   if(!GetIndicatorValues(fastMA, slowMA, filterMA, macdHist, macdBarsNeeded))
      return;

   double closePrice = iClose(_Symbol, _Period, 1); // 当前图表周期的前一根K线收盘价

   //--- 5. 检查现有持仓
   bool hasPosition = PositionSelect(_Symbol);
   if(hasPosition)
   {
      long posType = PositionGetInteger(POSITION_TYPE);

      // 5.1 过滤均线平仓(使用过滤线的周期)
      if(InpUseFilterClose)
      {
         bool closeSignal = false;
         if(posType == POSITION_TYPE_BUY && closePrice < filterMA[0])
            closeSignal = true;
         else if(posType == POSITION_TYPE_SELL && closePrice > filterMA[0])
            closeSignal = true;

         if(closeSignal)
         {
            CloseCurrentPosition();
            return;
         }
      }

      // 5.2 均线反转平仓反手(使用快慢线的周期)
      if(InpUseReverse)
      {
         double prevFast = GetPreviousMA(InpFastMA, InpFastMATimeframe);
         double prevSlow = GetPreviousMA(InpSlowMA, InpSlowMATimeframe);
         bool crossDown = (fastMA[0] < slowMA[0] && prevFast > prevSlow); // 死叉
         bool crossUp   = (fastMA[0] > slowMA[0] && prevFast < prevSlow); // 金叉

         if(posType == POSITION_TYPE_BUY && crossDown)
         {
            CloseCurrentPosition();
            if(!InpUseFilter || closePrice < filterMA[0])
               OpenSell();
            return;
         }
         else if(posType == POSITION_TYPE_SELL && crossUp)
         {
            CloseCurrentPosition();
            if(!InpUseFilter || closePrice > filterMA[0])
               OpenBuy();
            return;
         }
      }
      return;
   }
   else
   {
      //--- 6. 无持仓时,检测开仓信号
      if(g_dailyProfit >= InpDailyProfitLimit)
         return;
      if(!IsTradingTimeAllowed())
         return;

      bool macdBullish = false;
      bool macdBearish = false;

      if(InpUseMACDValidBars)
      {
         if(macdHist[0] > 0)
         {
            for(int i=1; i<=InpMACDValidBars && i<ArraySize(macdHist); i++)
            {
               if(macdHist[i] <= 0)
               {
                  macdBullish = true;
                  break;
               }
            }
         }
         else if(macdHist[0] < 0)
         {
            for(int i=1; i<=InpMACDValidBars && i<ArraySize(macdHist); i++)
            {
               if(macdHist[i] >= 0)
               {
                  macdBearish = true;
                  break;
               }
            }
         }
      }
      else
      {
         macdBullish = (macdHist[0] > 0);
         macdBearish = (macdHist[0] < 0);
      }

      //--- 均线方向判断(使用快慢线各自的周期)
      bool maBullish = (fastMA[0] > slowMA[0]);
      bool maBearish = (fastMA[0] < slowMA[0]);

      //--- 如果启用交叉开仓,则要求发生金叉/死叉(使用前一根值)
      if(InpOpenOnCross)
      {
         double prevFast = GetPreviousMA(InpFastMA, InpFastMATimeframe);
         double prevSlow = GetPreviousMA(InpSlowMA, InpSlowMATimeframe);
         bool crossUp   = (maBullish && prevFast <= prevSlow);   // 金叉
         bool crossDown = (maBearish && prevFast >= prevSlow);   // 死叉
         maBullish = crossUp;
         maBearish = crossDown;
      }

      bool buySignal  = maBullish && macdBullish;
      bool sellSignal = maBearish && macdBearish;

      if(InpUseFilter)
      {
         buySignal  = buySignal  && (closePrice > filterMA[0]);
         sellSignal = sellSignal && (closePrice < filterMA[0]);
      }

      if(buySignal)
         OpenBuy();
      else if(sellSignal)
         OpenSell();
   }
}

//+------------------------------------------------------------------+
//| 计算动态首单手数                                                 |
//+------------------------------------------------------------------+
double CalculateDynamicLot()
{
   double balance = AccountInfoDouble(ACCOUNT_BALANCE);
   double lot = balance / InpMoneyPerLot * InpBaseLot;
   return NormalizeLot(lot);
}

//+------------------------------------------------------------------+
//| 检查交易时间是否允许开首单                                       |
//+------------------------------------------------------------------+
bool IsTradingTimeAllowed()
{
   MqlDateTime dt;
   TimeToStruct(TimeCurrent(), dt);
   int currentMinutes = dt.hour * 60 + dt.min;
   int startMinutes = ParseTimeToMinutes(InpStartTime);
   int stopMinutes  = ParseTimeToMinutes(InpStopTime);

   if(startMinutes < stopMinutes)
      return (currentMinutes >= startMinutes && currentMinutes < stopMinutes);
   else if(startMinutes > stopMinutes)
      return (currentMinutes >= startMinutes || currentMinutes < stopMinutes);
   else
      return true; // 开始等于暂停,全天允许
}

//+------------------------------------------------------------------+
//| 解析 "HH:MM" 字符串为分钟数                                      |
//+------------------------------------------------------------------+
int ParseTimeToMinutes(string timeStr)
{
   string parts[];
   if(StringSplit(timeStr, ':', parts) != 2)
      return 0;
   int hours = (int)StringToInteger(parts[0]);
   int minutes = (int)StringToInteger(parts[1]);
   if(hours < 0 || hours > 23 || minutes < 0 || minutes > 59)
      return 0;
   return hours * 60 + minutes;
}

//+------------------------------------------------------------------+
//| 检查时间字符串格式是否正确                                       |
//+------------------------------------------------------------------+
bool IsValidTimeFormat(string timeStr)
{
   string parts[];
   if(StringSplit(timeStr, ':', parts) != 2)
      return false;
   int hours = (int)StringToInteger(parts[0]);
   int minutes = (int)StringToInteger(parts[1]);
   if(hours < 0 || hours > 23 || minutes < 0 || minutes > 59)
      return false;
   return true;
}

//+------------------------------------------------------------------+
//| 获取指标数据(前一根K线,各自周期)                              |
//+------------------------------------------------------------------+
bool GetIndicatorValues(double &fastMA[], double &slowMA[], double &filterMA[], double &macdHist[], int macdBars)
{
   // 使用各自指定的时间框架创建MA句柄
   int fastHandle = iMA(_Symbol, InpFastMATimeframe, InpFastMA, 0, InpMAMethod, PRICE_CLOSE);
   int slowHandle = iMA(_Symbol, InpSlowMATimeframe, InpSlowMA, 0, InpMAMethod, PRICE_CLOSE);
   int filterHandle = iMA(_Symbol, InpFilterMATimeframe, InpFilterMA, 0, InpMAMethod, PRICE_CLOSE);
   int macdHandle = iOsMA(_Symbol, _Period, InpMACDFast, InpMACDSlow, InpMACDSignal, PRICE_CLOSE); // MACD仍用当前图表周期

   if(fastHandle == INVALID_HANDLE || slowHandle == INVALID_HANDLE ||
      filterHandle == INVALID_HANDLE || macdHandle == INVALID_HANDLE)
   {
      Print("指标创建失败");
      return false;
   }

   // 从各周期均线复制数据(shift=1,即前一根K线)
   if(CopyBuffer(fastHandle, 0, 1, 1, fastMA) != 1 ||
      CopyBuffer(slowHandle, 0, 1, 1, slowMA) != 1 ||
      CopyBuffer(filterHandle, 0, 1, 1, filterMA) != 1)
   {
      Print("均线数据复制失败");
      IndicatorRelease(fastHandle);
      IndicatorRelease(slowHandle);
      IndicatorRelease(filterHandle);
      IndicatorRelease(macdHandle);
      return false;
   }

   ArrayResize(macdHist, macdBars);
   if(CopyBuffer(macdHandle, 0, 1, macdBars, macdHist) != macdBars)
   {
      Print("MACD数据复制失败,需要", macdBars, "根,实际复制不足");
      IndicatorRelease(fastHandle);
      IndicatorRelease(slowHandle);
      IndicatorRelease(filterHandle);
      IndicatorRelease(macdHandle);
      return false;
   }

   IndicatorRelease(fastHandle);
   IndicatorRelease(slowHandle);
   IndicatorRelease(filterHandle);
   IndicatorRelease(macdHandle);
   return true;
}

//+------------------------------------------------------------------+
//| 获取指定均线的上一根K线值(用于交叉判断,指定周期)              |
//+------------------------------------------------------------------+
double GetPreviousMA(int period, ENUM_TIMEFRAMES tf)
{
   double buf[];
   int handle = iMA(_Symbol, tf, period, 0, InpMAMethod, PRICE_CLOSE);
   if(handle == INVALID_HANDLE) return 0;
   // 复制前两根K线中的第二根(即shift=2,索引2对应更早一根,我们取上一根即shift=1?注意:当前值使用shift=1,前一根值应使用shift=2)
   // 但GetIndicatorValues中当前值用了shift=1,因此前一根值应使用shift=2
   if(CopyBuffer(handle, 0, 2, 1, buf) != 1)
   {
      IndicatorRelease(handle);
      return 0;
   }
   IndicatorRelease(handle);
   return buf[0];
}

//+------------------------------------------------------------------+
//| 开多单                                                           |
//+------------------------------------------------------------------+
void OpenBuy()
{
   if(g_dailyProfit >= InpDailyProfitLimit)
      return;
   if(!IsTradingTimeAllowed())
      return;

   // 重置移动止损变量(新仓)
   g_trailingStopPrice = 0.0;
   g_extremePrice = 0.0;
   g_trailingTriggered = false;

   double lots = NormalizeLot(g_currentLot);
   double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   if(trade.Buy(lots, _Symbol, price, 0, 0, "DualMA_MACD EA Buy"))
      Print("开多成功,手数: ", lots);
   else
      Print("开多失败: ", trade.ResultRetcode());
}

//+------------------------------------------------------------------+
//| 开空单                                                           |
//+------------------------------------------------------------------+
void OpenSell()
{
   if(g_dailyProfit >= InpDailyProfitLimit)
      return;
   if(!IsTradingTimeAllowed())
      return;

   // 重置移动止损变量(新仓)
   g_trailingStopPrice = 0.0;
   g_extremePrice = 0.0;
   g_trailingTriggered = false;

   double lots = NormalizeLot(g_currentLot);
   double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   if(trade.Sell(lots, _Symbol, price, 0, 0, "DualMA_MACD EA Sell"))
      Print("开空成功,手数: ", lots);
   else
      Print("开空失败: ", trade.ResultRetcode());
}

//+------------------------------------------------------------------+
//| 平掉当前持仓并更新下一手数和每日盈利                             |
//+------------------------------------------------------------------+
void CloseCurrentPosition()
{
   if(!PositionSelect(_Symbol))
      return;

   long posTicket = PositionGetInteger(POSITION_TICKET);
   double posProfit = PositionGetDouble(POSITION_PROFIT);
   double posLots = PositionGetDouble(POSITION_VOLUME);

   if(trade.PositionClose(posTicket))
   {
      // 更新下一笔开仓手数
      if(posProfit > 0)
      {
         // 盈利:重置为动态计算的首单手数
         g_currentLot = CalculateDynamicLot();
      }
      else
      {
         // 亏损:在上一手数基础上固定增加步长
         g_currentLot = MathMin(g_currentLot + InpLotIncrement, InpMaxLots);
         g_currentLot = NormalizeLot(g_currentLot);
      }

      // 确保不小于最小手数
      double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
      if(g_currentLot < minLot) g_currentLot = minLot;

      // 累计当日盈利(已实现)
      g_dailyProfit += posProfit;
   }
   else
      Print("平仓失败,票据: ", posTicket, " 错误: ", trade.ResultRetcode());

   // 平仓后重置移动止损变量
   g_trailingStopPrice = 0.0;
   g_extremePrice = 0.0;
   g_trailingTriggered = false;
}

//+------------------------------------------------------------------+
//| 检查固定止盈止损(实时)                                         |
//+------------------------------------------------------------------+
void CheckFixedSLTP()
{
   if(!PositionSelect(_Symbol))
      return;

   long posType = PositionGetInteger(POSITION_TYPE);
   double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
   double currentPrice;
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   double profitPoints = 0;

   if(posType == POSITION_TYPE_BUY)
   {
      currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
      profitPoints = (currentPrice - openPrice) / point;
   }
   else if(posType == POSITION_TYPE_SELL)
   {
      currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      profitPoints = (openPrice - currentPrice) / point;
   }
   else
      return;

   // 移动止损触发后,仅禁用固定止损,固定止盈仍然有效
   bool checkStopLoss = true;
   if(InpUseTrailingStop && g_trailingTriggered)
      checkStopLoss = false;

   // 固定止盈(始终检查)
   if(InpTakeProfit > 0 && profitPoints >= InpTakeProfit)
   {
      CloseCurrentPosition();
      return;
   }

   // 固定止损(仅在未触发移动止损时检查)
   if(checkStopLoss && InpStopLoss > 0 && profitPoints <= -InpStopLoss)
   {
      CloseCurrentPosition();
      return;
   }
}

//+------------------------------------------------------------------+
//| 检查移动止损(实时)                                             |
//+------------------------------------------------------------------+
void CheckTrailingStop()
{
   if(!InpUseTrailingStop)
      return;
   if(!PositionSelect(_Symbol))
      return;

   long posType = PositionGetInteger(POSITION_TYPE);
   double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
   double currentPrice;
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   double profitPoints;

   if(posType == POSITION_TYPE_BUY)
   {
      currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
      profitPoints = (currentPrice - openPrice) / point;

      // 达到触发条件后开始跟踪
      if(profitPoints >= InpTrailingStart)
      {
         // 更新最高价
         if(!g_trailingTriggered || currentPrice > g_extremePrice)
         {
            g_extremePrice = currentPrice;
            g_trailingTriggered = true;
            g_trailingStopPrice = g_extremePrice - InpTrailingStep * point;
         }
         // 检查是否触及止损
         if(currentPrice <= g_trailingStopPrice)
         {
            CloseCurrentPosition();
            return;
         }
      }
   }
   else if(posType == POSITION_TYPE_SELL)
   {
      currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      profitPoints = (openPrice - currentPrice) / point;

      if(profitPoints >= InpTrailingStart)
      {
         // 更新最低价
         if(!g_trailingTriggered || currentPrice < g_extremePrice)
         {
            g_extremePrice = currentPrice;
            g_trailingTriggered = true;
            g_trailingStopPrice = g_extremePrice + InpTrailingStep * point;
         }
         // 检查是否触及止损
         if(currentPrice >= g_trailingStopPrice)
         {
            CloseCurrentPosition();
            return;
         }
      }
   }
}

//+------------------------------------------------------------------+
//| 手数规范化                                                       |
//+------------------------------------------------------------------+
double NormalizeLot(double lot)
{
   double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double step   = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);

   lot = MathMax(minLot, MathMin(maxLot, lot));
   lot = MathFloor(lot / step) * step;
   return lot;
}
//+------------------------------------------------------------------+

21572ca1-fbe5-4ba6-9ace-93744d7da1ba.png
21572ca1-fbe5-4ba6-9ace-93744d7da1ba.png
举报

评论 使用道具

精彩评论1

lovechenzhen
D
| 发表于 4 分钟前 | 显示全部楼层
简单介绍一下 啊
举报

点赞 评论 使用道具

发新帖
EA交易
您需要登录后才可以评论 登录 | 立即注册