MT5EA / 策略编译通过 · EX5 43KB 自动发布 9 4

精控呗441极端信号EA

老吴·1 天前发布

双K线突破+ATR过滤,完整风控体系(移动止盈、动态止损、日盈亏限制)

精控呗441极端信号EA

效果示意图 · AI 根据源码绘制,仅示意画了哪些元素,细节以实际运行为准

PrecisionControl441_ExtremeSignal.mq5642 行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
#property copyright "XAUOF指标工坊"
#property version   "1.00"

#include <Trade\Trade.mqh>

//--- 输入参数
input group "========== 信号参数 =========="
input int    InpMagicNumber = 260813;              // 魔术号码
input bool   InpUseDoubleCandle = true;            // 启用双K线确认模式
input int    InpMinCandlePoints = 225;             // 做多最小K线幅度(点)
input int    InpMultiMinHighReq = 225;             // 做多最小涨幅要求(点)
input int    InpMultiMinLowReq = 225;              // 做多最小跌幅要求(点)
input bool   InpUseATRFilter = true;               // 启用ATR过滤器
input int    InpATRPeriod = 12;                    // ATR计算周期
input double InpMinATRPoints = 330.0;              // 最小ATR点数(只交易大于此值)
input bool   InpAutoAdjustTime = true;             // 启用自动周期适应
input int    InpTradeStartHour = 0;                // 开始交易的时间(服务器时间),0=全天交易
input int    InpTradeEndHour = 0;                  // 结束交易的时间(服务器时间),0=全天交易

input group "========== 止损止盈 =========="
input double InpFixedStopLoss = 0.0;               // 固定止损(0=自动计算)
input double InpRiskPercent = 1.0;                 // 风险百分比(0.1-20.0)
input double InpNormalStopPoints = 750;            // 普通止损(点)
input double InpNormalTakeProfitPoints = 5650;     // 普通止盈(点)
input bool   InpUseTimeBasedParams = false;        // 启用时段特定参数
input double InpPeakStopLoss = 850;                // 高峰柔时段止损(08:00-16:00)
input double InpPeakTakeProfit = 4250;             // 高峰柔时段止盈
input bool   InpUseMovingStop = true;              // 启用移动止损
input int    InpMovingStopActivate = 1350;         // 移动止盈盘启动点数(0=禁用,浮盈达此点数后启动)
input int    InpMovingStopDistance = 200;          // 移动止盈回撤点数(0=禁用,从最高/最低回撤此点数平仓)
input bool   InpUseTrailStop = true;               // 启用动态追踪止损
input int    InpTrailActivateProfit = 1600;        // 动态追踪启动盈利(点) - 盈利达到此点数后启动动态...
input int    InpTrailStopDistance = 300;           // 动态追踪距离(点) - 止损距离当前价格的点数

input group "========== 日盈亏控制 =========="
input double InpDailyProfitLimit = 12.0;           // 日盈利限制(%)
input double InpDailyLossLimit = 8.0;              // 日亏损限制(%)
input double InpDailyProfitAmount = 0.0;           // 日盈利金额限制($) (0=禁用)
input double InpDailyLossAmount = 0.0;             // 日亏损金额限制($) (0=禁用)
input int    InpMaxDailyTrades = 20;               // 最大日交易数
input bool   InpUseLossContinuousControl = true;   // 启用连续亏损控制

//--- 全局变量
CTrade trade;
int atrHandle = INVALID_HANDLE;
double atrBuffer[];
datetime lastBarTime = 0;
int dailyTradeCount = 0;
double dailyProfit = 0.0;
double dailyLoss = 0.0;
datetime lastResetDate = 0;
double consecutiveLoss = 0.0;
bool buyAllowed = true;
bool sellAllowed = true;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   // 设置魔术号
   trade.SetExpertMagicNumber(InpMagicNumber);
   trade.SetDeviationInPoints(10);
   trade.SetTypeFilling(ORDER_FILLING_FOK);
   if(trade.ResultRetcode() == TRADE_RETCODE_INVALID_FILL)
      trade.SetTypeFilling(ORDER_FILLING_IOC);
   
   // 创建ATR指标句柄
   if(InpUseATRFilter)
   {
      atrHandle = iATR(_Symbol, PERIOD_CURRENT, InpATRPeriod);
      if(atrHandle == INVALID_HANDLE)
      {
         Print("[初始化] ATR指标创建失败");
         return INIT_FAILED;
      }
   }
   
   ArraySetAsSeries(atrBuffer, true);
   
   Print("========== EA初始化成功 ==========");
   Print("[参数] 魔术号: ", InpMagicNumber);
   Print("[参数] 双K线确认: ", InpUseDoubleCandle ? "启用" : "禁用");
   Print("[参数] 最小K线幅度: ", InpMinCandlePoints, " 点");
   Print("[参数] ATR过滤: ", InpUseATRFilter ? "启用" : "禁用", ", 最小ATR: ", InpMinATRPoints, " 点");
   Print("[参数] 风险百分比: ", InpRiskPercent, "%");
   Print("[参数] 普通止损/止盈: ", InpNormalStopPoints, "/", InpNormalTakeProfitPoints, " 点");
   Print("[参数] 移动止损: ", InpUseMovingStop ? "启用" : "禁用", ", 启动点数: ", InpMovingStopActivate);
   Print("[参数] 动态追踪: ", InpUseTrailStop ? "启用" : "禁用", ", 启动盈利: ", InpTrailActivateProfit);
   Print("[参数] 日盈亏限制: +", InpDailyProfitLimit, "% / -", InpDailyLossLimit, "%");
   Print("[参数] 最大日交易数: ", InpMaxDailyTrades);
   Print("=================================");
   
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if(atrHandle != INVALID_HANDLE)
      IndicatorRelease(atrHandle);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   // 检查是否是新K线
   datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
   if(currentBarTime == lastBarTime)
      return;
   lastBarTime = currentBarTime;
   
   // 重置日统计
   ResetDailyStats();
   
   // 检查交易权限
   if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
   {
      Print("[跳过] 终端不允许交易");
      return;
   }
   
   if(!MQLInfoInteger(MQL_TRADE_ALLOWED))
   {
      Print("[跳过] EA不允许交易");
      return;
   }
   
   // 检查日盈亏限制
   if(!CheckDailyLimits())
      return;
   
   // 检查交易时间
   if(!CheckTradingTime())
      return;
   
   // 更新移动止损和动态追踪
   ManageOpenPositions();
   
   // 检查是否达到最大日交易数
   if(dailyTradeCount >= InpMaxDailyTrades)
   {
      Print("[风控] 今日交易数已达上限 ", InpMaxDailyTrades);
      return;
   }
   
   // 检查开仓信号
   int signal = CheckSignal();
   
   if(signal == 1 && buyAllowed)
   {
      if(!HasPosition(POSITION_TYPE_BUY))
         OpenPosition(ORDER_TYPE_BUY);
   }
   else if(signal == -1 && sellAllowed)
   {
      if(!HasPosition(POSITION_TYPE_SELL))
         OpenPosition(ORDER_TYPE_SELL);
   }
}

//+------------------------------------------------------------------+
//| 检查交易信号                                                      |
//+------------------------------------------------------------------+
int CheckSignal()
{
   // 获取最近3根K线数据
   double close[], open[], high[], low[];
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(open, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);
   
   if(CopyClose(_Symbol, PERIOD_CURRENT, 0, 3, close) < 3) return 0;
   if(CopyOpen(_Symbol, PERIOD_CURRENT, 0, 3, open) < 3) return 0;
   if(CopyHigh(_Symbol, PERIOD_CURRENT, 0, 3, high) < 3) return 0;
   if(CopyLow(_Symbol, PERIOD_CURRENT, 0, 3, low) < 3) return 0;
   
   // 获取ATR值
   double atrValue = 0;
   if(InpUseATRFilter)
   {
      if(BarsCalculated(atrHandle) < 2)
         return 0;
      
      if(CopyBuffer(atrHandle, 0, 0, 2, atrBuffer) < 2)
         return 0;
      
      atrValue = atrBuffer[0] / _Point;
      
      // ATR过滤
      if(atrValue < InpMinATRPoints)
      {
         return 0;
      }
   }
   
   // 计算第1根K线的涨跌幅(索引1是前一根已完成的K线)
   double candle1Range = MathAbs(close[1] - open[1]) / _Point;
   bool candle1Bullish = close[1] > open[1];
   bool candle1Bearish = close[1] < open[1];
   
   // 第1根K线必须满足最小幅度要求
   if(candle1Range < InpMinCandlePoints)
      return 0;
   
   // 双K线确认模式
   if(InpUseDoubleCandle)
   {
      // 做多信号:第1根看涨K线(收>开)且涨幅≥225点,第2根(当前K线)收盘突破第1根最高点
      if(candle1Bullish && candle1Range >= InpMultiMinHighReq)
      {
         if(close[0] > high[1])
         {
            Print("[信号] 做多信号触发 - 双K线突破,第1根涨幅:", (int)candle1Range, "点,第2根突破前高,ATR:", (int)atrValue, "点");
            return 1;
         }
      }
      
      // 做空信号:第1根看跌K线(收<开)且跌幅≥225点,第2根(当前K线)收盘突破第1根最低点
      if(candle1Bearish && candle1Range >= InpMultiMinLowReq)
      {
         if(close[0] < low[1])
         {
            Print("[信号] 做空信号触发 - 双K线突破,第1根跌幅:", (int)candle1Range, "点,第2根突破前低,ATR:", (int)atrValue, "点");
            return -1;
         }
      }
   }
   else
   {
      // 单K线模式(不需要第二根确认)
      if(candle1Bullish && candle1Range >= InpMultiMinHighReq)
      {
         Print("[信号] 做多信号触发 - 单K线模式,涨幅:", (int)candle1Range, "点,ATR:", (int)atrValue, "点");
         return 1;
      }
      
      if(candle1Bearish && candle1Range >= InpMultiMinLowReq)
      {
         Print("[信号] 做空信号触发 - 单K线模式,跌幅:", (int)candle1Range, "点,ATR:", (int)atrValue, "点");
         return -1;
      }
   }
   
   return 0;
}

//+------------------------------------------------------------------+
//| 开仓                                                              |
//+------------------------------------------------------------------+
void OpenPosition(ENUM_ORDER_TYPE orderType)
{
   double price = (orderType == ORDER_TYPE_BUY) ? 
                  SymbolInfoDouble(_Symbol, SYMBOL_ASK) : 
                  SymbolInfoDouble(_Symbol, SYMBOL_BID);
   
   // 计算止损止盈
   double sl = 0, tp = 0;
   CalculateStopLossTakeProfit(orderType, price, sl, tp);
   
   // 计算手数
   double lotSize = CalculateLotSize(sl, price);
   
   // 检查手数范围
   double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   
   lotSize = MathFloor(lotSize / lotStep) * lotStep;
   lotSize = MathMax(minLot, MathMin(maxLot, lotSize));
   
   // 执行开仓
   bool result = false;
   if(orderType == ORDER_TYPE_BUY)
      result = trade.Buy(lotSize, _Symbol, 0, sl, tp, "精控呗441");
   else
      result = trade.Sell(lotSize, _Symbol, 0, sl, tp, "精控呗441");
   
   if(result)
   {
      dailyTradeCount++;
      Print("[开仓] ", (orderType == ORDER_TYPE_BUY ? "做多" : "做空"), 
            " ", lotSize, " 手,价格:", price, ",止损:", sl, ",止盈:", tp);
   }
   else
   {
      Print("[开仓失败] 错误代码:", trade.ResultRetcode(), ",描述:", trade.ResultRetcodeDescription());
   }
}

//+------------------------------------------------------------------+
//| 计算止损止盈                                                      |
//+------------------------------------------------------------------+
void CalculateStopLossTakeProfit(ENUM_ORDER_TYPE orderType, double price, double &sl, double &tp)
{
   double stopLossPoints = InpNormalStopPoints;
   double takeProfitPoints = InpNormalTakeProfitPoints;
   
   // 如果启用时段特定参数
   if(InpUseTimeBasedParams)
   {
      MqlDateTime dt;
      TimeToStruct(TimeCurrent(), dt);
      if(dt.hour >= 8 && dt.hour < 16)
      {
         stopLossPoints = InpPeakStopLoss;
         takeProfitPoints = InpPeakTakeProfit;
      }
   }
   
   // 检查最小止损距离
   int stopsLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
   if(stopsLevel > 0)
   {
      stopLossPoints = MathMax(stopLossPoints, stopsLevel + 10);
      takeProfitPoints = MathMax(takeProfitPoints, stopsLevel + 10);
   }
   
   double point = _Point;
   
   if(orderType == ORDER_TYPE_BUY)
   {
      sl = price - stopLossPoints * point;
      tp = price + takeProfitPoints * point;
   }
   else
   {
      sl = price + stopLossPoints * point;
      tp = price - takeProfitPoints * point;
   }
   
   // 规范化价格
   int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
   sl = NormalizeDouble(sl, digits);
   tp = NormalizeDouble(tp, digits);
}

//+------------------------------------------------------------------+
//| 计算手数(基于风险百分比)                                         |
//+------------------------------------------------------------------+
double CalculateLotSize(double sl, double entryPrice)
{
   if(InpRiskPercent <= 0 || sl == 0)
      return SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   
   double balance = AccountInfoDouble(ACCOUNT_BALANCE);
   double riskAmount = balance * InpRiskPercent / 100.0;
   
   double slDistance = MathAbs(entryPrice - sl);
   double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   
   if(tickSize == 0 || slDistance == 0)
      return SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   
   double lotSize = riskAmount / (slDistance / tickSize * tickValue);
   
   return lotSize;
}

//+------------------------------------------------------------------+
//| 管理已开仓位(移动止损和动态追踪)                                 |
//+------------------------------------------------------------------+
void ManageOpenPositions()
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0) continue;
      
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue;
      
      ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
      double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
      double currentSL = PositionGetDouble(POSITION_SL);
      double currentTP = PositionGetDouble(POSITION_TP);
      double profit = PositionGetDouble(POSITION_PROFIT);
      
      double currentPrice = (posType == POSITION_TYPE_BUY) ? 
                           SymbolInfoDouble(_Symbol, SYMBOL_BID) : 
                           SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      
      double profitPoints = 0;
      if(posType == POSITION_TYPE_BUY)
         profitPoints = (currentPrice - openPrice) / _Point;
      else
         profitPoints = (openPrice - currentPrice) / _Point;
      
      bool needModify = false;
      double newSL = currentSL;
      double newTP = currentTP;
      
      // 移动止盈(回撤平仓)
      if(InpUseMovingStop && InpMovingStopActivate > 0 && InpMovingStopDistance > 0)
      {
         if(profitPoints >= InpMovingStopActivate)
         {
            // 计算从最高/最低价回撤的距离
            if(posType == POSITION_TYPE_BUY)
            {
               double newStopPrice = currentPrice - InpMovingStopDistance * _Point;
               if(newStopPrice > currentSL)
               {
                  newSL = NormalizeDouble(newStopPrice, _Digits);
                  needModify = true;
               }
            }
            else
            {
               double newStopPrice = currentPrice + InpMovingStopDistance * _Point;
               if(currentSL == 0 || newStopPrice < currentSL)
               {
                  newSL = NormalizeDouble(newStopPrice, _Digits);
                  needModify = true;
               }
            }
         }
      }
      
      // 动态追踪止损
      if(InpUseTrailStop && InpTrailActivateProfit > 0 && InpTrailStopDistance > 0)
      {
         if(profitPoints >= InpTrailActivateProfit)
         {
            if(posType == POSITION_TYPE_BUY)
            {
               double trailStop = currentPrice - InpTrailStopDistance * _Point;
               if(trailStop > currentSL)
               {
                  newSL = NormalizeDouble(trailStop, _Digits);
                  needModify = true;
               }
            }
            else
            {
               double trailStop = currentPrice + InpTrailStopDistance * _Point;
               if(currentSL == 0 || trailStop < currentSL)
               {
                  newSL = NormalizeDouble(trailStop, _Digits);
                  needModify = true;
               }
            }
         }
      }
      
      // 执行修改
      if(needModify)
      {
         if(trade.PositionModify(ticket, newSL, currentTP))
         {
            Print("[止损调整] 订单:", ticket, ",新止损:", newSL, ",当前盈利:", (int)profitPoints, "点");
         }
      }
   }
}

//+------------------------------------------------------------------+
//| 检查是否有指定类型的持仓                                          |
//+------------------------------------------------------------------+
bool HasPosition(ENUM_POSITION_TYPE posType)
{
   for(int i = 0; i < PositionsTotal(); i++)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0) continue;
      
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue;
      if((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == posType)
         return true;
   }
   return false;
}

//+------------------------------------------------------------------+
//| 检查交易时间                                                      |
//+------------------------------------------------------------------+
bool CheckTradingTime()
{
   if(InpTradeStartHour == 0 && InpTradeEndHour == 0)
      return true;
   
   MqlDateTime dt;
   TimeToStruct(TimeCurrent(), dt);
   
   // 周末不交易
   if(dt.day_of_week == 0 || dt.day_of_week == 6)
      return false;
   
   int currentHour = dt.hour;
   
   if(InpTradeStartHour < InpTradeEndHour)
   {
      if(currentHour < InpTradeStartHour || currentHour >= InpTradeEndHour)
         return false;
   }
   else if(InpTradeStartHour > InpTradeEndHour)
   {
      if(currentHour < InpTradeStartHour && currentHour >= InpTradeEndHour)
         return false;
   }
   
   return true;
}

//+------------------------------------------------------------------+
//| 重置日统计                                                        |
//+------------------------------------------------------------------+
void ResetDailyStats()
{
   MqlDateTime dt;
   TimeToStruct(TimeCurrent(), dt);
   datetime currentDate = StringToTime(StringFormat("%04d.%02d.%02d", dt.year, dt.mon, dt.day));
   
   if(currentDate != lastResetDate)
   {
      dailyTradeCount = 0;
      dailyProfit = 0.0;
      dailyLoss = 0.0;
      buyAllowed = true;
      sellAllowed = true;
      consecutiveLoss = 0.0;
      lastResetDate = currentDate;
      
      Print("[日统计] 新交易日开始: ", TimeToString(currentDate, TIME_DATE));
   }
}

//+------------------------------------------------------------------+
//| 检查日盈亏限制                                                    |
//+------------------------------------------------------------------+
bool CheckDailyLimits()
{
   // 计算今日盈亏
   CalculateDailyProfitLoss();
   
   double balance = AccountInfoDouble(ACCOUNT_BALANCE);
   
   // 检查日盈利限制(百分比)
   if(InpDailyProfitLimit > 0)
   {
      double profitPercent = (dailyProfit / balance) * 100.0;
      if(profitPercent >= InpDailyProfitLimit)
      {
         Print("[风控] 今日盈利已达 ", DoubleToString(profitPercent, 2), "%,停止开仓");
         return false;
      }
   }
   
   // 检查日盈利限制(金额)
   if(InpDailyProfitAmount > 0 && dailyProfit >= InpDailyProfitAmount)
   {
      Print("[风控] 今日盈利已达 $", DoubleToString(dailyProfit, 2), ",停止开仓");
      return false;
   }
   
   // 检查日亏损限制(百分比)
   if(InpDailyLossLimit > 0)
   {
      double lossPercent = (dailyLoss / balance) * 100.0;
      if(lossPercent >= InpDailyLossLimit)
      {
         Print("[风控] 今日亏损已达 ", DoubleToString(lossPercent, 2), "%,停止开仓");
         return false;
      }
   }
   
   // 检查日亏损限制(金额)
   if(InpDailyLossAmount > 0 && dailyLoss >= InpDailyLossAmount)
   {
      Print("[风控] 今日亏损已达 $", DoubleToString(dailyLoss, 2), ",停止开仓");
      return false;
   }
   
   // 检查连续亏损控制
   if(InpUseLossContinuousControl && consecutiveLoss >= 200)
   {
      Print("[风控] 连续亏损已达 ", (int)consecutiveLoss, " 点,暂停开仓");
      return false;
   }
   
   return true;
}

//+------------------------------------------------------------------+
//| 计算今日盈亏                                                      |
//+------------------------------------------------------------------+
void CalculateDailyProfitLoss()
{
   dailyProfit = 0.0;
   dailyLoss = 0.0;
   
   MqlDateTime dt;
   TimeToStruct(TimeCurrent(), dt);
   datetime todayStart = StringToTime(StringFormat("%04d.%02d.%02d 00:00", dt.year, dt.mon, dt.day));
   
   // 统计当前持仓的盈亏
   for(int i = 0; i < PositionsTotal(); i++)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0) continue;
      
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue;
      
      double profit = PositionGetDouble(POSITION_PROFIT);
      if(profit > 0)
         dailyProfit += profit;
      else
         dailyLoss += MathAbs(profit);
   }
   
   // 统计今日已平仓订单
   HistorySelect(todayStart, TimeCurrent());
   for(int i = 0; i < HistoryDealsTotal(); i++)
   {
      ulong ticket = HistoryDealGetTicket(i);
      if(ticket == 0) continue;
      
      if(HistoryDealGetString(ticket, DEAL_SYMBOL) != _Symbol) continue;
      if(HistoryDealGetInteger(ticket, DEAL_MAGIC) != InpMagicNumber) continue;
      if(HistoryDealGetInteger(ticket, DEAL_ENTRY) != DEAL_ENTRY_OUT) continue;
      
      double profit = HistoryDealGetDouble(ticket, DEAL_PROFIT);
      if(profit > 0)
      {
         dailyProfit += profit;
         consecutiveLoss = 0.0; // 重置连续亏损
      }
      else
      {
         dailyLoss += MathAbs(profit);
         consecutiveLoss += MathAbs(profit / _Point); // 累加连续亏损点数
      }
   }
}

作品说明

这是一个基于双K线突破确认的EA,配合ATR波动率过滤和完整的风控体系(移动止盈、动态追踪止损、日盈亏限制、连续亏损控制)。核心逻辑:第一根K线单边涨跌≥225点,第二根K线突破前高/前低,且ATR≥330点时开仓。

使用说明

  1. 安装方法:将代码保存为 PrecisionControl441_ExtremeSignal.mq5,放入 MQL5\Experts 目录,重启 MT5 或刷新导航器,拖到图表上。
  1. 核心参数:
  • InpMinCandlePoints(225):第一根K线的最小涨跌幅度,低于此值不开仓
  • InpMinATRPoints(330):ATR波动率过滤,市场波动小于此值不交易
  • InpMovingStopActivate(1350):浮盈达到此点数后启动移动止盈
  • InpTrailActivateProfit(1600):浮盈达到此点数后启动动态追踪止损
  1. 风控设置:
  • InpDailyProfitLimit(12%)和 InpDailyLossLimit(8%):日盈亏达限后自动停止开仓
  • InpMaxDailyTrades(20):每日最多开仓次数,防止过度交易
  • InpRiskPercent(1.0%):每单风险占账户权益的百分比,自动计算手数
  1. 交易时间:默认全天交易(参数为0),如需限制交易时段,将 InpTradeStartHour 设为 8、InpTradeEndHour 设为 16(服务器时间08:00-16:00)
  1. 信号逻辑:前一根K线单边涨跌≥225点,当前K线收盘突破前一根的高/低点,且ATR≥330点时开仓;同方向已有持仓时跳过信号
  1. 止损止盈:默认普通止损750点、止盈5650点;启用时段参数后,08:00-16:00 使用高峰柔时段的850/4250点
  1. 日志查看:在"工具箱-专家"标签页查看详细日志,所有开仓、平仓、风控决策都有记录,便于回溯和调试
  1. 注意事项:首次运行建议先在模拟盘测试,观察信号触发频率和盈亏分布;实盘前务必确认服务器时间与交易时段设置一致;ATR过滤值需根据品种特性调整(黄金波动大,外汇可能需降低阈值)

AI 解读根据源码逐行分析写成,供参考,以实际运行为准

这是什么

这是一个 MetaTrader 5 自动交易 EA,核心逻辑是双 K 线突破确认:第一根 K 线单边涨跌幅度达到设定点数(默认 225 点),第二根 K 线收盘价突破第一根的高点或低点时开仓,同时用 ATR 指标过滤低波动行情。内置完整的风控体系,包括移动止盈、动态追踪止损、日盈亏限制、最大日交易数和连续亏损控制,适合追踪趋势突破行情。

逻辑原理

入场信号生成:EA 在每根新 K 线形成时检查信号。启用双 K 线模式时(默认启用),做多信号需要满足:前一根已完成的 K 线是阳线(收盘价 > 开盘价)且涨幅 ≥ 225 点,当前 K 线收盘价突破前一根的最高点;做空信号则相反,前一根是阴线且跌幅 ≥ 225 点,当前 K 线收盘价跌破前一根的最低点。如果关闭双 K 线模式,只要单根 K 线涨跌幅达标即可触发信号,不需要第二根确认。

ATR 波动率过滤:启用后(默认启用,周期 12),EA 会计算当前 ATR 值并转换为点数,只有当 ATR ≥ 330 点时才允许开仓。这个设计用来避免在低波动、盘整行情中频繁交易。

止损止盈计算:默认使用普通止损 750 点、止盈 5650 点。如果启用时段特定参数(InpUseTimeBasedParams),在服务器时间 08:00-16:00 期间会切换为高峰柔时段的止损 850 点、止盈 4250 点。止损会自动检查交易商的最小止损距离(SYMBOL_TRADE_STOPS_LEVEL),如果设定值小于要求,会自动加 10 点补偿。

手数计算:根据风险百分比(默认 1.0%)和止损距离自动计算手数。公式是:风险金额 = 账户余额 × 风险百分比,手数 = 风险金额 ÷ (止损点数 × 每点价值)。计算后的手数会按交易商的手数步长规范化,并限制在最小手数和最大手数之间。

移动止盈:当浮盈达到 1350 点后启动,止损会跟随价格移动,始终保持距离当前价 200 点。例如做多单,价格涨到浮盈 1350 点后,止损会设在当前卖价 - 200 点的位置,价格继续上涨时止损继续上移,价格回撤 200 点即平仓锁定利润。

动态追踪止损:当浮盈达到 1600 点后启动,止损跟随价格保持 300 点距离。和移动止盈的区别是距离更宽松,适合让利润继续奔跑。两种机制可同时启用,EA 会在每根新 K 线时检查并更新止损。

日盈亏限制:每天开始时重置统计。EA 会累计今日所有已平仓订单和当前持仓的盈亏,如果盈利达到账户余额的 12%(或设定的固定金额),或亏损达到 8%(或固定金额),会停止开新仓但不影响已有持仓。同时限制每日最多开仓 20 次。

连续亏损控制:启用后(默认启用),EA 会累计连续亏损的点数。每次平仓盈利时重置为 0,亏损时累加亏损点数。当连续亏损达到 200 点时暂停开仓,直到下一个交易日重置。

持仓管理:同方向已有持仓时不会重复开仓。EA 通过魔术号(默认 260813)识别自己的订单,不会影响同账户其他 EA 或手动单。

参数说明

信号参数

  • InpMagicNumber(260813):魔术号,用于识别 EA 的订单,多个 EA 同时运行时需设不同值
  • InpUseDoubleCandle(true):是否启用双 K 线确认,true = 需要第二根 K 线突破确认,false = 单根 K 线达标即开仓
  • InpMinCandlePoints(225):第一根 K 线的最小涨跌幅度,低于此值不产生信号。减小会增加信号频率但可能增加假信号
  • InpMultiMinHighReq(225):做多时第一根 K 线的最小涨幅要求,通常和 InpMinCandlePoints 保持一致
  • InpMultiMinLowReq(225):做空时第一根 K 线的最小跌幅要求
  • InpUseATRFilter(true):是否启用 ATR 波动率过滤,建议启用以避免低波动行情
  • InpATRPeriod(12):ATR 计算周期,越大越平滑但反应越慢
  • InpMinATRPoints(330.0):最小 ATR 点数,低于此值不交易。黄金可用默认值,外汇品种波动小可能需降到 100-200
  • InpAutoAdjustTime(true):自动周期适应开关,代码中未实现具体逻辑,可忽略
  • InpTradeStartHour(0):开始交易的小时(服务器时间),0 = 全天交易。例如设 8 则只在 08:00 后交易
  • InpTradeEndHour(0):结束交易的小时,0 = 全天。配合开始时间使用,例如 8-16 限制在 08:00-16:00 交易

止损止盈

  • InpFixedStopLoss(0.0):固定止损,代码中未使用此参数,实际止损由后面参数决定
  • InpRiskPercent(1.0):每单风险占账户余额的百分比,范围 0.1-20.0。1.0 表示每单风险 1% 余额
  • InpNormalStopPoints(750):普通止损点数,非时段参数模式下使用
  • InpNormalTakeProfitPoints(5650):普通止盈点数
  • InpUseTimeBasedParams(false):是否启用时段特定参数,true 时在 08:00-16:00 使用下面两个值
  • InpPeakStopLoss(850):高峰柔时段止损点数
  • InpPeakTakeProfit(4250):高峰柔时段止盈点数
  • InpUseMovingStop(true):是否启用移动止盈,建议启用以保护利润
  • InpMovingStopActivate(1350):移动止盈启动点数,浮盈达此值后开始跟随价格移动止损
  • InpMovingStopDistance(200):移动止盈回撤距离,止损始终保持距离当前价此点数
  • InpUseTrailStop(true):是否启用动态追踪止损
  • InpTrailActivateProfit(1600):动态追踪启动盈利点数,浮盈达此值后启动
  • InpTrailStopDistance(300):动态追踪距离,止损距离当前价的点数

日盈亏控制

  • InpDailyProfitLimit(12.0):日盈利限制百分比,达到账户余额的此百分比后停止开仓
  • InpDailyLossLimit(8.0):日亏损限制百分比
  • InpDailyProfitAmount(0.0):日盈利金额限制(美元),0 = 禁用。优先级和百分比并列,任一达标即停止
  • InpDailyLossAmount(0.0):日亏损金额限制
  • InpMaxDailyTrades(20):最大日交易数,达到后不再开新仓
  • InpUseLossContinuousControl(true):是否启用连续亏损控制,连续亏损 200 点后暂停开仓

怎么用

  1. 将代码保存为 .mq5 文件,放入 MT5 数据文件夹的 MQL5\Experts 目录
  2. 重启 MT5 或在导航器中右键"智能交易系统"选择刷新
  3. 将 EA 从导航器拖到图表上,在弹出的参数窗口调整设置
  4. 确保勾选"允许自动交易"和"允许 DLL 导入"(虽然此 EA 未用 DLL,但部分经纪商要求勾选)
  5. 图表右上角显示笑脸图标表示 EA 运行正常
  6. 在"工具箱-交易"标签页查看持仓和订单,"专家"标签页查看详细日志

信号查看:EA 在每根新 K 线形成时检查信号,满足条件会在日志中输出"[信号] 做多/做空信号触发"并显示具体数据。开仓成功会输出"[开仓]"日志,包含手数、价格、止损止盈。止损调整会输出"[止损调整]"日志。

建议设置:首次使用先在模拟盘测试至少一周,观察信号频率和盈亏分布。实盘时确认服务器时间与交易时段设置一致(可在 MT5 菜单"工具-选项-服务器"查看)。黄金(XAUUSD)可用默认参数,外汇对建议降低 ATR 阈值到 100-200 点,同时调整 K 线幅度要求到 50-100 点。

适用场景

适合的品种和周期:设计用于波动较大的品种,默认参数适合黄金(XAUUSD)的 H1 或 H4 周期。外汇主要货币对(EURUSD、GBPUSD 等)波动较小,需降低 ATR 和 K 线幅度阈值。指数(US30、NAS100)也可用,但需根据点值调整参数。不建议用于加密货币,因其极端波动可能触发过宽的止损。

适合的行情:趋势突破行情,尤其是盘整后的大幅单边突破。ATR 过滤确保只在高波动时交易,双 K 线确认机制减少假突破。不适合低波动盘整行情(ATR 自动过滤)和剧烈震荡行情(可能频繁触发信号但立即回撤)。

时段选择:默认全天交易。如果发现夜间或亚洲盘信号质量差,可设置 InpTradeStartHour = 8 和 InpTradeEndHour = 16 限制在欧美盘交易(具体时段需根据经纪商服务器时间调整)。启用时段特定参数后,高峰时段会使用更紧的止损止盈(850/4250 点),适合快进快出。

资金管理:1% 风险百分比适合保守交易者,激进者可提高到 2-3%,但需配合更严格的日亏损限制。日盈亏限制(12% 盈利 / 8% 亏损)可根据个人风险承受能力调整,建议盈利限制 > 亏损限制,以"让利润奔跑、尽早止损"为原则。

风险提示

信号滞后:双 K 线确认机制要等第二根 K 线收盘才开仓,可能错过突破初期的最佳入场点。如果追求更快入场,可关闭双 K 线模式(InpUseDoubleCandle = false),但会增加假信号风险。

假突破风险:单纯的价格突破容易遇到假突破,尤其在新闻发布或开盘时段。虽然 ATR 过滤和双 K 线确认能减少假信号,但无法完全避免。建议配合更大的时间周期使用(H4 比 M15 更可靠)。

止损距离:默认止损 750 点对黄金来说相对较宽(约 7.5 美元),如果行情剧烈波动可能造成较大单笔亏损。风险百分比和日亏损限制能控制总风险,但单笔亏损仍需心理承受。止损过窄(如改为 300 点)则容易被正常波动扫掉。

滑点和点差:EA 使用市价单开仓(trade.Buy 和 trade.Sell 不指定价格),在波动剧烈时可能遇到滑点。止损止盈也可能因为点差或流动性不足无法精确成交。建议选择点差稳定、执行速度快的经纪商。

移动止盈和追踪止损的平衡:两种机制同时启用时,移动止盈(1350 点启动,200 点回撤)比追踪止损(1600 点启动,300 点距离)更激进,可能在利润刚达标时就被提前平仓。如果希望让利润充分增长,可以只启用追踪止损,或调高移动止盈的启动点数。

连续亏损控制:200 点连续亏损后暂停开仓,相当于约 2-3 次止损(每次 750 点止损 / 风险百分比)。如果设置过严(如 100 点),可能在正常回撤中就被限制交易,错过后续盈利机会。

重绘问题:此 EA 不存在重绘,因为所有信号判断都基于已完成的 K 线(close[1]、high[1]、low[1]),不会随着当前 K 线的变化而改变历史信号。

优化和过拟合:默认参数(225 点 K 线幅度、330 点 ATR)可能是基于历史数据优化的结果。直接用于实盘前需在样本外数据测试,避免过拟合导致未来表现差。建议在至少 3-6 个月的历史数据上回测,并在模拟盘验证。

常见问题

这个 EA 会重绘吗?信号会变化吗?
不会重绘。所有信号判断都基于前一根已完成的 K 线,不会因为当前 K 线的波动而改变历史信号或已开仓位的依据。
默认参数适合什么周期和品种?
默认参数(225 点 K 线幅度、330 点 ATR、750 点止损)主要针对黄金(XAUUSD)的 H1 或 H4 周期。外汇对波动较小,建议降低 ATR 到 100-200 点,K 线幅度到 50-100 点。
可以只用移动止盈或追踪止损吗?
可以。在参数中将 `InpUseMovingStop` 或 `InpUseTrailStop` 设为 false 即可单独禁用。两者同时启用时,移动止盈更激进(启动点 1350、回撤 200),追踪止损更宽松(启动点 1600、距离 300)。
日亏损限制达到后会自动平仓吗?
不会。日盈亏限制只控制是否开新仓,不会主动平掉已有持仓。已开仓位仍按止损止盈或移动止损规则管理。如需自动平仓需自行修改代码。
ATR 过滤值 330 点是怎么来的?需要调整吗?
330 点是代码中设定的默认值,用于过滤低波动行情。黄金日均波动通常在 300-800 点,330 是中等阈值。如果信号过少可降到 200-250,如果假信号多可提高到 400-500,需根据回测和实盘表现调整。

Precision Control 441 Extreme Signal EA for MT5

An automated trading EA for MetaTrader 5 using double-candle breakout confirmation with ATR volatility filter. Features comprehensive risk management including trailing stop, dynamic stop-loss adjustment, daily profit/loss limits, and consecutive loss control.

Source code is shown above. Compiled EX5 files are available to VIP members.

评论

后可以留言,比如反馈用着怎么样、想加什么功能。

还没有人评论,来抢个沙发。