希望祠.同舟.量化.侦云取势势能研究中心

 找回密码
 立即注册

《周易.系辞》

君子藏器于身,待时而动!

《吕氏.春秋》

君子谋时而动,顺势而为!

《晏子·霸业因时而生》

识时务者为俊杰,通机变者为英豪!

《周易.系辞》

天之所助,吉无不利,天之所助者.顺天者也!

《史记.太史公自序》
天之势,浩浩汤汤,顺之者昌,逆之者亡!

《寒窑赋》

天不得时,日月无光.地 不得时,草木不长.

《周易.系辞》

刚柔者,立本者也!变通者,识时者也!

圣人云:

君子务本,本立而道生!

 《势胜学》

不知势,无以为人也.势易而未觉,必败焉!

《周易·系辞上传》:

富有之谓大业,日新之谓盛德!

《礼记.大学》:
格物致知,正心诚意,修身齐家!

《礼记.中庸》:

凡事预则立,不预则废!

查看: 206|回复: 0

[【优秀推荐】曲线完美收益中等] 顺势 + 放量突破 + 支持固定点数止损 + 交易时间段过滤

[复制链接]

773

主题

838

帖子

3021

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
3021

最佳新人活跃会员热心会员推广达人荣誉管理副站长之印官方管理员常务

admin 发表于 5 天前 | 显示全部楼层 |阅读模式
<MQL5>
//+------------------------------------------------------------------+
//|                                     Trend_Volume_Breakout_EA.mq5 |
//|        V8.2 - 顺势 + 放量突破 + 支持固定点数止损 + 交易时间段过滤 |
//+------------------------------------------------------------------+
#property copyright "Smart Money Logic"
#property version   "8.20"
#property strict
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
//--- 枚举:止损模式选择
enum ENUM_SL_MODE {
   SL_FIXED_POINTS = 0, // 模式1:使用固定点数 (Points)
   SL_ATR_DYNAMIC = 1   // 模式2:使用ATR动态波动率
};
//--- 输入参数
sinput group "==== 交易时间过滤 (平台服务器时间) ===="
input bool     InpUseTimeFilter = true;         // 开启时间过滤
input int      InpStartHour     = 12;           // 允许开仓起始小时 (0-23)
input int      InpEndHour       = 20;           // 允许开仓结束小时 (0-23)
sinput group "==== 趋势均线过滤 ===="
input bool     InpUseMaFilter   = true;         // 开启双均线过滤
input int      InpEmaFast       = 50;           // 快线 (判定短期趋势)
input int      InpEmaSlow       = 200;          // 慢线 (判定长期趋势)
sinput group "==== 价格与成交量突破设定 ===="
input int      InpBreakPeriod   = 20;           // 价格突破前 N 根K线的高/低点
input int      InpVolPeriod     = 20;           // 计算平均成交量的周期
input double   InpVolMultiplier = 1.5;          // 突破时的成交量必须大于平均量的倍数
sinput group "==== 止损与止盈模式选择 ===="
input ENUM_SL_MODE InpSlMode    = SL_FIXED_POINTS; // ★止损与移动止盈计算模式★
sinput group "==== [模式1] 固定点数设置 (Points) ===="
input int      InpFixedStopPts  = 300;          // 初始止损点数 (黄金: 300点=3美金差价)
input int      InpTrailStartPts = 200;          // 盈利达到多少点数后触发移动止盈
input int      InpTrailStepPts  = 300;          // 移动止盈保护距离点数
sinput group "==== [模式2] ATR动态设置 ===="
input int      InpAtrPeriod     = 14;           // ATR 周期
input double   InpInitialStopATR= 2.0;          // 初始止损 ATR 倍数
input double   InpTrailStartATR = 1.0;          // 盈利多少倍ATR触发移动止盈
input double   InpTrailStepATR  = 2.0;          // 移动止盈保护距离 ATR 倍数
sinput group "==== 仓位与风控设置 ===="
input bool     InpUseFixedLot   = true;         // 使用固定手数
input double   InpFixedLot      = 0.05;         // 固定手数
input double   InpRiskPercent   = 1.0;          // 动态风险比例 (%) (按止损距离计算)
input bool     InpUseTrailing   = true;         // 开启移动止盈 (让利润奔跑)
//--- 全局变量
CTrade         trade;
CPositionInfo  pos;
int            handle_atr, handle_ema_fast, handle_ema_slow;
//+------------------------------------------------------------------+
//| 初始化                                                            |
//+------------------------------------------------------------------+
int OnInit()
{
   trade.SetExpertMagicNumber(888999);
   trade.SetDeviationInPoints(50);
   trade.SetTypeFilling(ORDER_FILLING_IOC);

   // 初始化指标句柄
   handle_atr = iATR(_Symbol, PERIOD_CURRENT, InpAtrPeriod);
   handle_ema_fast = iMA(_Symbol, PERIOD_CURRENT, InpEmaFast, 0, MODE_EMA, PRICE_CLOSE);
   handle_ema_slow = iMA(_Symbol, PERIOD_CURRENT, InpEmaSlow, 0, MODE_EMA, PRICE_CLOSE);

   Print("V8.2 顺势突破+欧美盘时间过滤版 已启动!当前模式: ", (InpSlMode == SL_FIXED_POINTS ? "固定点数" : "ATR动态"));
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| 主逻辑 (每跳动一次都会执行)                                         |
//+------------------------------------------------------------------+
void OnTick()
{
   // 1. 实时更新移动止盈 (★非常重要:持仓单必须24小时全天候追踪利润,不受时间过滤影响★)
   ManageTrailingStop();
   // 2. 判断是否在允许的开仓时间段内
   bool can_trade = true;
   if(InpUseTimeFilter)
   {
      MqlDateTime dt;
      TimeToStruct(TimeCurrent(), dt); // 获取平台服务器当前时间

      // 检查当前小时是否在设定的区间内 (例如 12 到 20)
      if(InpStartHour < InpEndHour) {
         if(dt.hour < InpStartHour || dt.hour >= InpEndHour) can_trade = false;
      }
      // 支持跨夜时间段 (例如 22 到 02)
      else if(InpStartHour > InpEndHour) {
         if(dt.hour < InpStartHour && dt.hour >= InpEndHour) can_trade = false;
      }
      else {
         if(dt.hour != InpStartHour) can_trade = false;
      }
   }
   // 3. 开仓判定:只在K线收盘时执行
   static datetime last_bar = 0;
   datetime curr_bar = iTime(_Symbol, PERIOD_CURRENT, 0);
   if(curr_bar == 0 || curr_bar == last_bar) return;
   // 确保历史数据足够
   if(SeriesInfoInteger(_Symbol, PERIOD_CURRENT, SERIES_BARS_COUNT) < InpEmaSlow + 10) return;
   // 如果不在交易时间内,只更新 last_bar,不执行后续计算
   if(!can_trade)
   {
      last_bar = curr_bar;
      return;
   }
   // --- 获取刚收盘的K线数据 (索引 1) ---
   double close1 = iClose(_Symbol, PERIOD_CURRENT, 1);
   long   vol1   = iVolume(_Symbol, PERIOD_CURRENT, 1);
   if(close1 == 0) return;
   // --- 1. 判断均线趋势 ---
   double ema_fast[1], ema_slow[1];
   if(CopyBuffer(handle_ema_fast, 0, 1, 1, ema_fast) <= 0) return;
   if(CopyBuffer(handle_ema_slow, 0, 1, 1, ema_slow) <= 0) return;

   bool is_uptrend   = (ema_fast[0] > ema_slow[0] && close1 > ema_fast[0]);
   bool is_downtrend = (ema_fast[0] < ema_slow[0] && close1 < ema_fast[0]);
   if(!InpUseMaFilter) { is_uptrend = true; is_downtrend = true; }
   // --- 2. 判断价格是否创出 N 周期新高/新低 ---
   double highest_high = 0, lowest_low = 999999;
   for(int i = 2; i <= InpBreakPeriod + 1; i++) {
      double h = iHigh(_Symbol, PERIOD_CURRENT, i);
      double l = iLow(_Symbol, PERIOD_CURRENT, i);
      if(h > highest_high) highest_high = h;
      if(l < lowest_low)   lowest_low = l;
   }
   bool is_price_break_up   = (close1 > highest_high);
   bool is_price_break_down = (close1 < lowest_low);
   // --- 3. 判断是否爆量 ---
   long sum_vol = 0;
   for(int i = 2; i <= InpVolPeriod + 1; i++) {
      sum_vol += iVolume(_Symbol, PERIOD_CURRENT, i);
   }
   double avg_vol = (double)sum_vol / InpVolPeriod;
   bool is_volume_break = (vol1 > avg_vol * InpVolMultiplier);
   // --- 4. 计算止损距离 ---
   double sl_dist = 0;
   if(InpSlMode == SL_FIXED_POINTS)
   {
      sl_dist = InpFixedStopPts * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   }
   else
   {
      double atr_val[1];
      if(CopyBuffer(handle_atr, 0, 1, 1, atr_val) > 0) {
         sl_dist = atr_val[0] * InpInitialStopATR;
      }
   }
   // --- 5. 执行开仓逻辑 ---
   if(!PositionSelectByMagic(888999) && sl_dist > 0)
   {
      double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
      int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
      double min_stop = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
      if(min_stop == 0) min_stop = SymbolInfoDouble(_Symbol, SYMBOL_POINT) * 10;

      sl_dist = MathMax(sl_dist, min_stop * 2);
      // --- 做多 ---
      if(is_uptrend && is_price_break_up && is_volume_break)
      {
         double sl = NormalizeDouble(ask - sl_dist, digits);
         double tp = 0; // 全靠移动止盈出局

         double lots = GetSafeLot(ask - sl);
         if(lots > 0 && trade.Buy(lots, _Symbol, ask, sl, tp, "Breakout_Buy")) {
            PrintFormat(">>> 多头突破开仓!时间段符合。止损距离:%.3f", sl_dist);
            last_bar = curr_bar;
         }
      }

      // --- 做空 ---
      else if(is_downtrend && is_price_break_down && is_volume_break)
      {
         double sl = NormalizeDouble(bid + sl_dist, digits);
         double tp = 0;

         double lots = GetSafeLot(sl - bid);
         if(lots > 0 && trade.Sell(lots, _Symbol, bid, sl, tp, "Breakout_Sell")) {
            PrintFormat(">>> 空头突破开仓!时间段符合。止损距离:%.3f", sl_dist);
            last_bar = curr_bar;
         }
      }
   }
   else
   {
      last_bar = curr_bar;
   }
}
//+------------------------------------------------------------------+
//| 动态移动止盈逻辑 (持仓保护,全天24小时运行)                        |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
   if(!InpUseTrailing) return;

   double trail_dist = 0;
   double trigger_dist = 0;

   if(InpSlMode == SL_FIXED_POINTS)
   {
      trail_dist   = InpTrailStepPts * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
      trigger_dist = InpTrailStartPts * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   }
   else
   {
      double atr_val[1];
      if(CopyBuffer(handle_atr, 0, 1, 1, atr_val) <= 0) return;
      trail_dist   = atr_val[0] * InpTrailStepATR;
      trigger_dist = atr_val[0] * InpTrailStartATR;
   }

   if(trail_dist <= 0 || trigger_dist <= 0) return;

   for(int i = PositionsTotal()-1; i >= 0; i--)
   {
      if(pos.SelectByIndex(i))
      {
         if(pos.Magic() == 888999 && pos.Symbol() == _Symbol)
         {
            double open_price = pos.PriceOpen();
            double current_sl = pos.StopLoss();
            double current_price = pos.PriceCurrent();
            int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);

            if(pos.PositionType() == POSITION_TYPE_BUY)
            {
               if(current_price - open_price > trigger_dist)
               {
                  double new_sl = NormalizeDouble(current_price - trail_dist, digits);
                  if(new_sl > current_sl)
                  {
                     trade.PositionModify(pos.Ticket(), new_sl, pos.TakeProfit());
                  }
               }
            }
            else if(pos.PositionType() == POSITION_TYPE_SELL)
            {
               if(open_price - current_price > trigger_dist)
               {
                  double new_sl = NormalizeDouble(current_price + trail_dist, digits);
                  if(new_sl < current_sl || current_sl == 0)
                  {
                     trade.PositionModify(pos.Ticket(), new_sl, pos.TakeProfit());
                  }
               }
            }
         }
      }
   }
}
//+------------------------------------------------------------------+
//| 安全的手数计算 (自动适配固定止损的风险比例)                          |
//+------------------------------------------------------------------+
double GetSafeLot(double sl_dist)
{
   double min_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double max_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double step    = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);

   double lot = InpUseFixedLot ? InpFixedLot : min_lot;
   if(!InpUseFixedLot)
   {
      if(sl_dist <= 0) return min_lot;
      double tick_val = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
      double tick_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
      if(tick_val == 0 || tick_size == 0) return min_lot;

      double loss_per_lot = (sl_dist / tick_size) * tick_val;
      double risk_money = AccountInfoDouble(ACCOUNT_BALANCE) * (InpRiskPercent / 100.0);
      lot = risk_money / loss_per_lot;
   }
   lot = MathFloor(lot / step) * step;
   return MathMax(MathMin(lot, max_lot), min_lot);
}
//+------------------------------------------------------------------+
//| 检查是否已经持仓                                                  |
//+------------------------------------------------------------------+
bool PositionSelectByMagic(long magic)
{
   for(int i=PositionsTotal()-1; i>=0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket > 0)
         if(PositionGetInteger(POSITION_MAGIC) == magic && PositionGetString(POSITION_SYMBOL) == _Symbol)
            return true;
   }
   return false;
}
⏱️ 关于时间的两个重要提示:
这里的时间是“服务器时间”!
MT5的回测和运行时间都是依据交易平台的服务器时间(在 MT5 市场报价窗口顶部可以看到)。大多数主流外汇经纪商的服务器时区通常是 GMT+2 或 GMT+3。

如果经纪商时间是 12:00,对应的可能是北京时间的下午 17:00 左右。
所以请先查看你的平台服务器时间与北京时间的时差,来微调 InpStartHour 和 InpEndHour 的参数!
跨日时间也支持:
如果你以后想专门做夜盘,比如平台时间晚上 22:00 到第二天凌晨 02:00,你可以直接设置:InpStartHour = 22,InpEndHour = 2。代码内部已经自动处理了跨夜逻辑,不会报错。

现在这套系统:大级别均线定方向 + 放量突破出单 + 指定高波动时间段 + 固定点数硬止损 + 移动止盈拿波段,是一套可以直接上场实战的纯正右侧量化模型了!

回复

使用道具 举报

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

本版积分规则

关闭

站长推荐上一条 /5 下一条

人生者,生存也。
生存所需,财(钱)官(权)也。
财自食伤(子孙福德爻)生,
印星生身荫护,官财印全,适合人间生存也。
若命缺财,缺官,则失去生存权。
一旦脱离父母养育,将天天面临,
从绝望中醒来,又从绝望中睡去。来去如烟!

Archiver|小黑屋|希望祠.同舟.量化

GMT+8, 2026-4-2 02:35 , Processed in 0.023341 second(s), 20 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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