我是灵宝。自研的第三套EA今晚在大非农前后没有预期触发,让我很受打击。经过调

| 发表于 2026-6-5 22:57:46 | 显示全部楼层 |复制链接
我是灵宝。自研的第三套EA今晚在大非农前后没有预期触发,让我很受打击。经过调试查找总算解决了触发问题,一是自己粗心设错了年份,二是用错了函数它不符合经纪商的默认模式。也许是自己曾亲眼见识过一位高手用这种交易法做的非常好,所以尝试借鉴其主观手法和量价行为开发出EA来,目前也算是有点眉目了,尽管还很粗糙。按照承诺我把MQL5源码放这里了,供大家把玩下。纯属个人兴趣,请勿轻易实盘去赌行情,模拟盘玩玩还行。如果说它真的有价值的话,也必须经过N多次实盘验证后才会有结论。初来宝地,请各路高人多多批评指正!
//+------------------------------------------------------------------+
//|                          PriceAction_EA.mq5                      |
//|                       非农CPI事件驱动锁仓交易法                  |
//|                        纯价格行为模式 - 全自动                   |
//|                    开发者:ChinaLingbao@outlook.com              |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026"
#property version   "1.00"
#property strict
//+------------------------------------------------------------------+
//| 输入参数                                                          |
//+------------------------------------------------------------------+
input string   EventTimeStr = "2026.06.05 15:30";   // 测试时间(服务器) - 改成当前时间+3分钟
input double   FixedLotSize = 0.01;                 // 固定手数
input double   TakeProfitRatio = 3.0;               // 止盈盈亏比
input int      PreEventMinutes = 5;                 // 提前开仓分钟数
input int      PriceActionWaitSeconds = 120;        // 价格行为等待秒数(2分钟)
input int      PriceActionMinPoints = 500;          // 最小波动点数(5美元)
input double   VolumeSurgeRatio = 1.0;              // 成交量激增倍数
input int      RiskPoints = 1000;                   // 风险点数(1点=0.01美元, 1000点=10美元)
input int      Slippage = 100;                      // 允许滑点
input int      MagicNumber = 20260605;              // EA魔术号

//+------------------------------------------------------------------+
//| 全局变量                                                          |
//+------------------------------------------------------------------+
datetime eventTime;
datetime preTradeTime;
datetime priceActionDeadline;
datetime decisionTimeOut;
bool tradesOpened = false;
bool decisionMade = false;
int keptDirection = 0;
ulong buyTicket = 0;
ulong sellTicket = 0;
//+------------------------------------------------------------------+
//| 初始化                                                           |
//+------------------------------------------------------------------+
int OnInit()
{
   if(!ParseTime(EventTimeStr, eventTime))
   {
      Print("ERROR: Time format error. Use YYYY.MM.DD HH:MM");
      return INIT_FAILED;
   }
   preTradeTime = eventTime - PreEventMinutes * 60;
   priceActionDeadline = eventTime + PriceActionWaitSeconds;
   decisionTimeOut = eventTime + 600;
   Print("============================================");
   Print("PriceAction_EA Loaded");
   Print("Event Time: ", TimeToString(eventTime));
   Print("Open Time: ", TimeToString(preTradeTime));
   Print("Decision Deadline: ", TimeToString(priceActionDeadline));
   Print("============================================");
   Print("Risk Points: ", RiskPoints, " (", RiskPoints/100.0, " USD)");
   Print("Take Profit: ", RiskPoints * TakeProfitRatio, " (", RiskPoints * TakeProfitRatio/100.0, " USD)");
   Print("Price Threshold: ", PriceActionMinPoints, " (", PriceActionMinPoints/100.0, " USD)");
   Print("Volume Ratio: ", VolumeSurgeRatio);
   Print("============================================");
   return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| 反初始化                                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   Print("EA Unloaded");
}
//+------------------------------------------------------------------+
//| 主函数                                                            |
//+------------------------------------------------------------------+
void OnTick()
{
   datetime now = TimeCurrent();
   // 阶段1: 开锁仓
   if(!tradesOpened && now >= preTradeTime && now < eventTime)
   {
      OpenLockPositions();
      tradesOpened = true;
      Print("[OPEN] Lock positions completed");
   }
   // 阶段2: 决策
   if(tradesOpened && !decisionMade && now >= eventTime)
   {
      if(now > decisionTimeOut)
      {
         CloseAllPositions();
         decisionMade = true;
         Print("[TIMEOUT] Decision timeout, all closed");
         return;
      }
      
      if(now >= priceActionDeadline)
      {
         int decision = 0;
         string reason = "";
         decision = GetPriceActionDecision(reason);
         ExecuteDecision(decision, reason);
         decisionMade = true;
      }
   }
   // 阶段3: 持仓管理
   if(decisionMade && keptDirection != 0)
   {
      CheckTakeProfit();
      ManageTrailingStop();
   }
}
//+------------------------------------------------------------------+
//| 开锁仓订单                                                       |
//+------------------------------------------------------------------+
void OpenLockPositions()
{
   MqlTick tick;
   if(!SymbolInfoTick(_Symbol, tick))
   {
      Print("[ERROR] Failed to get tick");
      return;
   }
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   if(point == 0) point = 0.01;
   double riskPoints = RiskPoints;
   double tpPoints = riskPoints * TakeProfitRatio;
   double tpBuy = tick.ask + tpPoints * point;
   double tpSell = tick.bid - tpPoints * point;
   buyTicket = OpenOrder(ORDER_TYPE_BUY, FixedLotSize, tick.ask, 0, tpBuy, "Lock Buy");
   sellTicket = OpenOrder(ORDER_TYPE_SELL, FixedLotSize, tick.bid, 0, tpSell, "Lock Sell");
   if(buyTicket > 0)
      Print("[ORDER] Buy: ", buyTicket, " @ ", tick.ask);
   if(sellTicket > 0)
      Print("[ORDER] Sell: ", sellTicket, " @ ", tick.bid);
}
//+------------------------------------------------------------------+
//| 价格行为决策                                                      |
//+------------------------------------------------------------------+
int GetPriceActionDecision(string &reason)
{
   MqlRates rates[];
   if(CopyRates(_Symbol, PERIOD_M1, 0, 5, rates) < 4)
   {
      reason = "Failed to get rate data";
      return 0;
   }
   double eventOpen = rates[0].open;
   double currentClose = rates[0].close;
   long eventVolume = rates[0].tick_volume;
   long avgVolume = 0;
   for(int i = 1; i < 4; i++)
      avgVolume += rates.tick_volume;
   avgVolume /= 3;
   if(avgVolume == 0)
   {
      reason = "Volume data error";
      return 0;
   }
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   if(point == 0) point = 0.01;
   double priceChange = (currentClose - eventOpen) / point;
   double volumeRatio = (double)eventVolume / avgVolume;
   Print("[PA] Price Change: ", DoubleToString(priceChange, 0), " points");
   Print("[PA] Volume Ratio: ", DoubleToString(volumeRatio, 1));
   if(MathAbs(priceChange) >= PriceActionMinPoints && volumeRatio >= VolumeSurgeRatio)
   {
      if(priceChange > 0)
      {
         reason = "Price UP + Volume SURGE -> BULLISH";
         return 1;
      }
      else
      {
         reason = "Price DOWN + Volume SURGE -> BEARISH";
         return -1;
      }
   }
   reason = "Conditions not met, skip trading";
   return 0;
}
//+------------------------------------------------------------------+
//| 执行决策                                                         |
//+------------------------------------------------------------------+
void ExecuteDecision(int decision, string reason)
{
   Print("[DECISION] ", reason);
   if(decision == 1)
   {
      CloseOrder(sellTicket);
      keptDirection = 1;
      Print("[EXECUTE] Keep LONG, closed SHORT");
   }
   else if(decision == -1)
   {
      CloseOrder(buyTicket);
      keptDirection = -1;
      Print("[EXECUTE] Keep SHORT, closed LONG");
   }
   else
   {
      CloseOrder(buyTicket);
      CloseOrder(sellTicket);
      keptDirection = 0;
      Print("[EXECUTE] No clear signal, all closed");
   }
}
//+------------------------------------------------------------------+
//| 检查止盈                                                         |
//+------------------------------------------------------------------+
void CheckTakeProfit()
{
   if(keptDirection == 0) return;
   ulong ticket = (keptDirection == 1) ? buyTicket : sellTicket;
   if(!PositionSelectByTicket(ticket)) return;
   double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
   double currentPrice = (keptDirection == 1) ?
                         SymbolInfoDouble(_Symbol, SYMBOL_BID) :
                         SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   if(point == 0) point = 0.01;
   double profitPoints = (keptDirection == 1) ?
                         (currentPrice - openPrice) / point :
                         (openPrice - currentPrice) / point;
   double targetPoints = RiskPoints * TakeProfitRatio;
   if(profitPoints >= targetPoints)
   {
      CloseOrder(ticket);
      Print("[TAKE PROFIT] ", profitPoints, " points");
      keptDirection = 0;
   }
}
//+------------------------------------------------------------------+
//| 移动止损 - 已添加 IOC 填充模式                                     |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
   if(keptDirection == 0) return;
   ulong ticket = (keptDirection == 1) ? buyTicket : sellTicket;
   if(!PositionSelectByTicket(ticket)) return;
   double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
   double currentSL = PositionGetDouble(POSITION_SL);
   double currentPrice = (keptDirection == 1) ?
                         SymbolInfoDouble(_Symbol, SYMBOL_BID) :
                         SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   if(point == 0) point = 0.01;
   double profitPoints = (keptDirection == 1) ?
                         (currentPrice - openPrice) / point :
                         (openPrice - currentPrice) / point;
   int trailStart = 300;
   int trailStep = 50;
   if(profitPoints >= trailStart)
   {
      double newSL;
      if(keptDirection == 1)
         newSL = currentPrice - trailStep * point;
      else
         newSL = currentPrice + trailStep * point;
      if(newSL > currentSL)
      {
         MqlTradeRequest req = {};
         MqlTradeResult res = {};
         req.action = TRADE_ACTION_SLTP;
         req.position = ticket;
         req.sl = newSL;
         req.tp = PositionGetDouble(POSITION_TP);
         req.symbol = _Symbol;
         req.magic = MagicNumber;
         req.type_filling = ORDER_FILLING_IOC;  // ✅ 已添加填充模式
         if(OrderSend(req, res))
         {
            if(res.retcode == TRADE_RETCODE_DONE)
               Print("[TRAIL STOP] Updated to: ", DoubleToString(newSL, _Digits));
            else
               Print("[TRAIL STOP] Failed, retcode: ", res.retcode);
         }
         else
         {
            Print("[TRAIL STOP] OrderSend error: ", GetLastError());
         }
      }
   }
}
//+------------------------------------------------------------------+
//| 全部平仓                                                         |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
   CloseOrder(buyTicket);
   CloseOrder(sellTicket);
   keptDirection = 0;
}
//+------------------------------------------------------------------+
//| 开单函数 - 使用 IOC 填充模式                                       |
//+------------------------------------------------------------------+
ulong OpenOrder(ENUM_ORDER_TYPE type, double volume, double price, double sl, double tp, string comment)
{
   MqlTradeRequest req = {};
   MqlTradeResult res = {};
   req.action = TRADE_ACTION_DEAL;
   req.symbol = _Symbol;
   req.volume = volume;
   req.type = type;
   req.price = price;
   req.sl = sl;
   req.tp = tp;
   req.deviation = Slippage;
   req.magic = MagicNumber;
   req.comment = comment;
   req.type_filling = ORDER_FILLING_IOC;  // ✅ Immediate or Cancel - TMGM 支持
   if(OrderSend(req, res))
   {
      if(res.retcode == TRADE_RETCODE_DONE)
         return res.order;
      else
         Print("Open order failed, retcode: ", res.retcode);
   }
   else
   {
      Print("Open order failed, error: ", GetLastError());
   }
   return 0;
}
//+------------------------------------------------------------------+
//| 平仓函数 - 添加 IOC 填充模式                                       |
//+------------------------------------------------------------------+
void CloseOrder(ulong ticket)
{
   if(ticket == 0) return;
   if(!PositionSelectByTicket(ticket)) return;
   MqlTradeRequest req = {};
   MqlTradeResult res = {};
   req.action = TRADE_ACTION_DEAL;
   req.symbol = PositionGetString(POSITION_SYMBOL);
   req.volume = PositionGetDouble(POSITION_VOLUME);
   req.deviation = Slippage;
   req.position = ticket;
   req.type_filling = ORDER_FILLING_IOC;  // ✅ 已添加填充模式
   ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
   if(posType == POSITION_TYPE_BUY)
   {
      req.type = ORDER_TYPE_SELL;
      req.price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   }
   else
   {
      req.type = ORDER_TYPE_BUY;
      req.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   }
   if(OrderSend(req, res))
   {
      if(res.retcode == TRADE_RETCODE_DONE)
         Print("[CLOSE] Order ", ticket, " closed");
      else
         Print("[CLOSE] Failed, retcode: ", res.retcode);
   }
   else
   {
      Print("[CLOSE] OrderSend error: ", GetLastError());
   }
}
//+------------------------------------------------------------------+
//| 解析时间                                                         |
//+------------------------------------------------------------------+
bool ParseTime(string str, datetime &out)
{
   if(StringLen(str) < 16) return false;
   int year = (int)StringToInteger(StringSubstr(str, 0, 4));
   int mon  = (int)StringToInteger(StringSubstr(str, 5, 2));
   int day  = (int)StringToInteger(StringSubstr(str, 8, 2));
   int hour = (int)StringToInteger(StringSubstr(str, 11, 2));
   int min  = (int)StringToInteger(StringSubstr(str, 14, 2));
   if(year < 2000 || mon < 1 || mon > 12 || day < 1 || day > 31) return false;
   if(hour < 0 || hour > 23 || min < 0 || min > 59) return false;
   MqlDateTime dt = {0};
   dt.year = year;
   dt.mon = mon;
   dt.day = day;
   dt.hour = hour;
   dt.min = min;
   dt.sec = 0;
   out = StructToTime(dt);
   return true;
}
//+------------------------------------------------------------------+
举报

评论 使用道具

精彩评论3

djav
DD
| 发表于 2026-6-30 22:05:52 来自手机 | 显示全部楼层
真正的大神
举报

点赞 评论 使用道具

chinalingbao
DD
 楼主 | 发表于 2026-6-30 22:43:13 | 显示全部楼层

这位大神叫林阳,曾经开直播带单,你猜最后怎么着?可能没听说过这种故事的人永远都猜不到我的答案。反正靠主观实盘做交易的人像林阳这样的高手,我这辈子只见识过他一个,尽管结局很遗憾。
举报

点赞 1 评论 使用道具

sqsm11
D
| 发表于 2026-7-9 16:09:34 | 显示全部楼层
结局怎么了
举报

点赞 评论 使用道具

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