diff --git a/20260516-突破策略/20260516_Breakout.mq5 b/20260516-突破策略/20260516_Breakout.mq5 index c677e62..407a1d7 100644 --- a/20260516-突破策略/20260516_Breakout.mq5 +++ b/20260516-突破策略/20260516_Breakout.mq5 @@ -23,7 +23,6 @@ input double InpStopLossPercent = 0.02; // 止损百分比(%) input int InpMinStopLossPoints = 10; // 最小止损点数 input double InpRiskRewardRatio = 1.2; // 盈亏比 input bool InpUseTrailingStop = false; // 使用移动止损 -input int InpMaxHoldingMinutes = 5; // 最大持仓时间(分钟) input group "=== 仓位管理参数 ===" input bool InpUseCompounding = true; // 使用复利模式 @@ -40,6 +39,7 @@ input group "=== 调试选项 ===" input bool InpShowDebugInfo = false; // 显示调试信息 input bool InpShowMarkers = true; // 显示极值点标记 input int InpMagicNumber = 20260516; // EA魔术号 +input bool InpCloseManualOrders = true; // 禁止手工单(自动平掉) //+------------------------------------------------------------------+ //| 全局变量 | @@ -84,6 +84,7 @@ bool OpenPosition(ENUM_ORDER_TYPE order_type, double wave_high, double wave_low, double CalculateLotSize(); void ManagePositions(); void CheckTrailingStop(ulong ticket); +void CheckAndCloseManualOrders(); //+------------------------------------------------------------------+ //| Expert initialization function | @@ -122,7 +123,6 @@ int OnInit() Print("反向突破容忍度: 0% (禁用 - 保持原有逻辑)"); Print("止损:", InpStopLossPercent, "% (最小", InpMinStopLossPoints, "点) | 盈亏比:", InpRiskRewardRatio); Print("移动止损:", (InpUseTrailingStop ? "启用" : "禁用")); - Print("最大持仓时间:", InpMaxHoldingMinutes, "分钟"); Print("最大开仓手数:", InpMaxPositions); Print("单方向持仓限制:", (InpOnePositionPerDirection ? "启用 (每方向最多1单)" : "禁用")); if(InpConsecutiveLosses > 0 && InpFreezeBarCount > 0) @@ -131,6 +131,7 @@ int OnInit() Print("连续亏损保护: 禁用"); Print("手数模式:", (InpUseCompounding ? "复利" : "固定"), InpUseCompounding ? StringFormat(" (每500$开%.2f手)", InpLotsPer500) : StringFormat(" (%.2f手)", InpFixedLots)); + Print("禁止手工单:", (InpCloseManualOrders ? "启用 (自动平掉手工单)" : "禁用")); Print("========================================"); return(INIT_SUCCEEDED); @@ -228,13 +229,17 @@ void OnTradeTransaction(const MqlTradeTransaction& trans, //+------------------------------------------------------------------+ void OnTick() { - // 1. 更新最新有效波段 + // 1. 检查并关闭手工单(如果启用) + if(InpCloseManualOrders) + CheckAndCloseManualOrders(); + + // 2. 更新最新有效波段 UpdateLatestValidWave(); - // 2. 检查开仓信号 + // 3. 检查开仓信号 CheckOpenSignals(); - // 3. 管理已有持仓 + // 4. 管理已有持仓 ManagePositions(); } @@ -835,17 +840,6 @@ void ManagePositions() if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue; - // 检查最大持仓时间 - datetime open_time = (datetime)PositionGetInteger(POSITION_TIME); - int holding_minutes = (int)((TimeCurrent() - open_time) / 60); - - if(holding_minutes >= InpMaxHoldingMinutes) { - ulong ticket = PositionGetTicket(i); - trade.PositionClose(ticket); - Print("持仓时间超限,平仓 - Ticket:", ticket, " 持仓时长:", holding_minutes, "分钟"); - continue; - } - // 检查移动止损 ulong ticket = PositionGetTicket(i); CheckTrailingStop(ticket); @@ -908,3 +902,32 @@ void CheckTrailingStop(ulong ticket) } } } + +//+------------------------------------------------------------------+ +//| 检查并关闭手工单 | +//+------------------------------------------------------------------+ +void CheckAndCloseManualOrders() +{ + for(int i = PositionsTotal() - 1; i >= 0; i--) { + if(!PositionSelectByTicket(PositionGetTicket(i))) + continue; + + // 只处理本品种 + if(PositionGetString(POSITION_SYMBOL) != _Symbol) + continue; + + // 检查magic number:0表示手工单 + long magic = PositionGetInteger(POSITION_MAGIC); + if(magic == 0) { + ulong ticket = PositionGetTicket(i); + + // 平仓手工单 + if(trade.PositionClose(ticket)) { + Print("【禁止手工单】已平掉手工单 - Ticket:", ticket, + " 类型:", (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY ? "多单" : "空单")); + } else { + Print("【禁止手工单】平仓失败 - Ticket:", ticket, " 错误:", GetLastError()); + } + } + } +} diff --git a/20260516-突破策略/README.md b/20260516-突破策略/README.md index 74e05bb..e18d257 100644 --- a/20260516-突破策略/README.md +++ b/20260516-突破策略/README.md @@ -49,6 +49,9 @@ - 连续亏损次数触发冷冻:默认 **3**(设为0可禁用该功能) - 冷冻K线根数:默认 **60**(设为0可禁用该功能) +### 手工单管理参数 +- 禁止手工单:默认 **true**(自动平掉所有手工单) + ## 补充说明 ### 突破K线处理 @@ -209,6 +212,85 @@ - 仅阻止新开仓,不会强制平仓现有持仓 - 可配合"单方向持仓限制"等功能使用 +### 禁止手工单功能说明 ⭐ + +**问题背景:** +在EA自动交易过程中,手工开单可能会干扰EA的整体策略逻辑和风险管理,导致: +- 破坏EA的仓位管理计划 +- 影响资金管理和风险控制 +- 与EA策略产生冲突 + +**功能说明:** +- 启用后,EA会自动检测并平掉所有手工单(magic number = 0的订单) +- 仅处理当前品种(_Symbol)的手工单 +- 平仓后会在日志中打印详细信息 + +**识别原理:** +在MQL5中,订单都有一个magic number(魔术号)属性: +- **手工单**:magic number = 0(默认值) +- **EA开单**:magic number = EA设定的值(本策略默认20260516) + +**参数说明:** +- **true(默认)**:启用保护,自动平掉手工单 + - 每个Tick检查一次 + - 发现手工单立即平仓 + - 打印平仓日志:`【禁止手工单】已平掉手工单 - Ticket:xxx 类型:多单/空单` + +- **false**:允许手工单与EA单共存 + - 手工单和EA单可以同时存在 + - 手工单不会被EA管理(不会移动止损等) + - 适合需要手工干预的场景 + +**使用场景:** + +1. **完全自动化交易**(推荐设置:true) + - 避免误操作影响EA策略 + - 确保严格执行EA逻辑 + - 防止情绪化交易 + +2. **半自动化交易**(设置:false) + - 允许手工单和EA单并存 + - 可以手工补仓或对冲 + - 需要手动管理手工单的风险 + +**示例:** + +``` +参数设置:禁止手工单 = true + +情况1:用户在MT5手动开了1个多单 +- EA在下一个Tick检测到该订单的magic=0 +- 自动平掉该订单 +- 打印:【禁止手工单】已平掉手工单 - Ticket:12345 类型:多单 + +情况2:EA自动开了1个多单(magic=20260516) +- EA检测到magic != 0,确认是EA自己开的单 +- 不会平仓,正常管理(移动止损等) + +情况3:其他EA开的单(magic=99999) +- EA检测到magic != 0,确认不是手工单 +- 不会平仓,但也不会管理 +``` + +**注意事项:** + +1. **仅处理当前品种** + - 只平掉当前EA运行品种的手工单 + - 不会影响其他品种的手工单 + +2. **实时检测** + - 每个Tick都会检查 + - 手工单会在极短时间内被平掉 + +3. **与其他功能的关系** + - 不影响EA自身的开仓逻辑 + - 不影响EA的风险管理功能 + - 可配合所有其他参数使用 + +4. **平仓失败处理** + - 如果平仓失败(网络问题等),会打印错误日志 + - 下一个Tick会继续尝试平仓 + ### 反向突破容忍度(新功能)⭐ **问题背景:** diff --git a/20260516-突破策略/XAU_1H.set b/20260516-突破策略/XAU_1H.set new file mode 100644 index 0000000..8e715d6 Binary files /dev/null and b/20260516-突破策略/XAU_1H.set differ diff --git a/20260516-突破策略/XAU_1min.set b/20260516-突破策略/XAU_1min.set new file mode 100644 index 0000000..df9dea5 Binary files /dev/null and b/20260516-突破策略/XAU_1min.set differ diff --git a/KVB-50059417/Gold Snap 500$.set b/KVB-50059417/Gold Snap 500$.set index aeb1076..24d9d16 100644 Binary files a/KVB-50059417/Gold Snap 500$.set and b/KVB-50059417/Gold Snap 500$.set differ