找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
首页论坛EAHub 外汇论坛
查看: 14|回复: 0

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

[复制链接]

4万

主题

54

回帖

4万

积分

管理员

积分
45954
发表于 2026-8-25 19:28:00 | 显示全部楼层 |阅读模式
我是灵宝。自研的第三套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("riceAction_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("rice 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 = "rice UP + Volume SURGE -> BULLISH";

         return 1;

      }

      else

      {

         reason = "rice 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;

}

//+------------------------------------------------------------------+
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|EAHub外汇论坛

GMT+8, 2026-10-11 19:43 , Processed in 0.108537 second(s), 19 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表