//+------------------------------------------------------------------+ //| CEntryExecutor.mqh | //| v35.0 - Final Unified Strategy Engine | //+------------------------------------------------------------------+ #ifndef CENTRYEXECUTOR_MQH #define CENTRYEXECUTOR_MQH #include "CEnumSharing.mqh" #include "CZoneManager.mqh" #include "CStructureAnalyzer.mqh" #include "SMC_DataStructures.mqh" class CEntryExecutor { private: CZoneManager* m_zone_manager_ptr; CStructureAnalyzer* m_analyzer_ptr; int m_max_trades_per_zone; // Toggles for each independent strategy bool m_use_continuation_retest; bool m_use_reversal_retest; bool m_use_breakout_entry; bool m_use_fakeout_entry; bool m_use_major_swing_momentum_entry; bool m_use_sfp_entry; bool m_use_sweep_and_poi_entry; bool m_use_scale_in_on_breakout; bool m_use_fresh_fvg_retest_entry; // Toggles for retest confirmation patterns bool m_use_simple_reclaim_pattern; bool m_use_2_bar_confirmation; bool m_use_3_bar_confirmation; // Toggles for specific exit conditions int m_breakout_retest_number; // Toggles for Stochastic entry confirmation bool m_use_sto_for_entry_confirmation; int m_sto_overbought; int m_sto_oversold; // Toggles for advanced FVG retest confirmation ENUM_RETEST_CONFIRMATION_MODE m_retest_confirmation_mode; double m_retest_min_atr_moveaway; SwingPoint* GetPreviousSwing(int start_index, ENUM_SWING_TYPE type_to_find, CArrayObj* swings_all) { if(start_index <= 0 || swings_all == NULL) return NULL; for(int i = start_index - 1; i >= 0; i--) { SwingPoint *swing = swings_all.At(i); if(swing != NULL && swing.status != STATUS_NOISE && swing.type == type_to_find) { return swing; } } return NULL; } // --- UPDATED with deep diagnostics --- bool IsStoInExtremeZone(int start_idx, int end_idx, ENUM_DIRECTION direction, CDataContext &context) { // ถ้าปิดฟิลเตอร์นี้ ให้ถือว่าผ่านเสมอ if(!m_use_sto_for_entry_confirmation) return true; PrintFormat("--- [Sto Check] Looking for %s signal in %d-bar window...", EnumToString(direction), (end_idx - start_idx + 1)); bool primary_condition_met = false; datetime triggering_time = 0; double triggering_sto_value = 0.0; // --- 1. ตรวจสอบว่ามีแท่งเทียนใดๆ ในกรอบเวลาที่เข้าเงื่อนไขหลักหรือไม่ (Primary Condition) --- for(int i = start_idx; i <= end_idx; i++) { if(i < 0 || i >= context.bars_to_load) continue; double sto_value = context.sto_main_buffer[i]; if(direction == DIRECTION_BULLISH && sto_value <= m_sto_oversold) { primary_condition_met = true; triggering_time = context.rates[i].time; triggering_sto_value = sto_value; break; // เจอแล้ว ไม่ต้องหาต่อ } if(direction == DIRECTION_BEARISH && sto_value >= m_sto_overbought) { primary_condition_met = true; triggering_time = context.rates[i].time; triggering_sto_value = sto_value; break; // เจอแล้ว ไม่ต้องหาต่อ } } if(!primary_condition_met) { Print(" - [Primary Check]: FAILED. No candle in window met OB/OS condition."); return false; } else { PrintFormat(" - [Primary Check]: PASSED. Triggered by candle at %s with Sto value: %.2f", TimeToString(triggering_time), triggering_sto_value); } // --- 2. ถ้าเงื่อนไขหลักผ่าน ให้มาตรวจสอบเงื่อนไข "Veto" ที่แท่งยืนยันสุดท้าย --- // แท่งยืนยันสุดท้ายคือแท่งที่ index = 1 เสมอ double confirmation_candle_sto = context.sto_main_buffer[1]; bool veto_condition_met = false; if(direction == DIRECTION_BULLISH && confirmation_candle_sto >= m_sto_overbought) { // กำลังจะ Buy แต่แท่งยืนยันกลับ Overbought . สัญญาณอันตราย, ใช้สิทธิ์ Veto veto_condition_met = true; } else if(direction == DIRECTION_BEARISH && confirmation_candle_sto <= m_sto_oversold) { // กำลังจะ Sell แต่แท่งยืนยันกลับ Oversold . สัญญาณอันตราย, ใช้สิทธิ์ Veto veto_condition_met = true; } if(veto_condition_met) { PrintFormat(" - [Veto Check]: FAILED. Confirmation candle Sto (%.2f) is in opposite extreme zone.", confirmation_candle_sto); } else { PrintFormat(" - [Veto Check]: PASSED. Confirmation candle Sto (%.2f) is not in opposite zone.", confirmation_candle_sto); } // --- 3. สรุปผลสุดท้าย --- bool final_result = (primary_condition_met && !veto_condition_met); PrintFormat(" >>> Final Sto Filter Result: %s", (string)final_result); return final_result; } bool IsSimpleReclaimPattern(CDataContext &context, CSignalZone* zone) { if(zone == NULL || ArraySize(context.rates) < 2) return false; MqlRates last_bar = context.rates[1]; if(zone.direction == DIRECTION_BULLISH) { bool retested = last_bar.low <= zone.top; bool is_bullish_candle = last_bar.close > last_bar.open; bool closed_above_zone = last_bar.close > zone.top; bool pattern_ok = (retested && is_bullish_candle && closed_above_zone); // for bullish case if(!pattern_ok) return false; // Final Check: Stochastic Confirmation return IsStoInExtremeZone(1, 1, zone.direction, context); } else if(zone.direction == DIRECTION_BEARISH) { bool retested = last_bar.high >= zone.bottom; bool is_bearish_candle = last_bar.close < last_bar.open; bool closed_below_zone = last_bar.close < zone.bottom; bool pattern_ok = (retested && is_bearish_candle && closed_below_zone); if(!pattern_ok) return false; // Final Check: Stochastic Confirmation return IsStoInExtremeZone(1, 1, zone.direction, context); } return false; } // --- Reusable Candle Pattern Library --- bool Is2BarPowerCandle(CDataContext &context, CSignalZone* zone) { if(zone == NULL || ArraySize(context.rates) < 3) return false; MqlRates last_bar = context.rates[1]; MqlRates prev_bar = context.rates[2]; if(zone.direction == DIRECTION_BULLISH) { bool retested = prev_bar.low <= zone.top; if(!retested) return false; bool is_bar1_bullish = last_bar.close > last_bar.open; double body1 = MathAbs(last_bar.close - last_bar.open); double body2 = MathAbs(prev_bar.close - prev_bar.open); bool bar1_closed_above = last_bar.close > zone.top; bool bar1_is_power_candle = (body2 > 0) ? (body1 > (body2 * 2.0)) : (body1 > 0); bool pattern_ok = (is_bar1_bullish && bar1_closed_above && bar1_is_power_candle); // for bullish case if(!pattern_ok) return false; // Final Check: Stochastic Confirmation over a 2-bar window return IsStoInExtremeZone(1, 2, zone.direction, context); } else if(zone.direction == DIRECTION_BEARISH) { bool retested = prev_bar.high >= zone.bottom; if(!retested) return false; bool is_bar1_bearish = last_bar.close < last_bar.open; double body1 = MathAbs(last_bar.close - last_bar.open); double body2 = MathAbs(prev_bar.close - prev_bar.open); bool bar1_closed_below = last_bar.close < zone.bottom; bool bar1_is_power_candle = (body2 > 0) ? (body1 > (body2 * 2.0)) : (body1 > 0); bool pattern_ok = (is_bar1_bearish && bar1_closed_below && bar1_is_power_candle); // for bullish case if(!pattern_ok) return false; // Final Check: Stochastic Confirmation over a 2-bar window return IsStoInExtremeZone(1, 2, zone.direction, context); } return false; } bool Candle_Engulfing(CDataContext &context, ENUM_DIRECTION direction) { if(ArraySize(context.rates) < 3) return false; MqlRates last_bar = context.rates[1]; MqlRates prev_bar = context.rates[2]; if(direction == DIRECTION_BULLISH) { bool is_bar1_bullish = last_bar.close > last_bar.open; double body1 = MathAbs(last_bar.close - last_bar.open); double body2 = MathAbs(prev_bar.close - prev_bar.open); bool bar1_is_power_candle = (body2 > 0) ? (body1 > (body2 * 2.0)) : (body1 > 0); bool pattern_ok = (is_bar1_bullish && bar1_is_power_candle); // for bullish case if(!pattern_ok) return false; return true; } else if(direction == DIRECTION_BEARISH) { bool is_bar1_bearish = last_bar.close < last_bar.open; double body1 = MathAbs(last_bar.close - last_bar.open); double body2 = MathAbs(prev_bar.close - prev_bar.open); bool bar1_is_power_candle = (body2 > 0) ? (body1 > (body2 * 2.0)) : (body1 > 0); bool pattern_ok = (is_bar1_bearish && bar1_is_power_candle); // for bullish case if(!pattern_ok) return false; return true; } return false; } bool Is3BarReversal(CDataContext &context, CSignalZone* zone) { if(zone == NULL || ArraySize(context.rates) < 4) return false; if(zone.direction == DIRECTION_BULLISH) { bool isRetesting = (context.rates[1].low <= zone.top) || (context.rates[2].low <= zone.top) || (context.rates[3].low <= zone.top); bool bar3_valid = (context.rates[3].open > context.rates[3].close) && (context.rates[3].close > zone.top); bool bar2_valid = (context.rates[2].open < context.rates[2].close) && (context.rates[2].close > zone.top); double highest_point = MathMax(context.rates[2].high, context.rates[3].high); bool bar1_valid = (context.rates[1].open < context.rates[1].close) && (context.rates[1].close > highest_point); bool pattern_ok = (isRetesting && bar3_valid && bar2_valid && bar1_valid); // for bullish case if(!pattern_ok) return false; // Final Check: Stochastic Confirmation over a 3-bar window return IsStoInExtremeZone(1, 3, zone.direction, context); } else if(zone.direction == DIRECTION_BEARISH) { bool isRetesting = (context.rates[1].high >= zone.bottom) || (context.rates[2].high >= zone.bottom) || (context.rates[3].high >= zone.bottom); bool bar3_valid = (context.rates[3].open < context.rates[3].close) && (context.rates[3].close < zone.bottom); bool bar2_valid = (context.rates[2].open > context.rates[2].close) && (context.rates[2].close < zone.bottom); double lowest_point = MathMin(context.rates[2].low, context.rates[3].low); bool bar1_valid = (context.rates[1].open > context.rates[1].close) && (context.rates[1].close < lowest_point); bool pattern_ok = (isRetesting && bar3_valid && bar2_valid && bar1_valid); // for bullish case if(!pattern_ok) return false; // Final Check: Stochastic Confirmation over a 3-bar window return IsStoInExtremeZone(1, 3, zone.direction, context); } return false; } // --- Strategy: First Touch on a Fresh FVG (Detective Version) --- CSignalZone* CheckForFreshFvgRetest(CDataContext &context) { if(ArraySize(context.rates) < 2) return NULL; CArrayObj* zone_list = m_zone_manager_ptr.GetSignificantZones(); if(zone_list == NULL) return NULL; for(int i = 0; i < zone_list.Total(); i++) { CSignalZone* zone = zone_list.At(i); // --- ด่านที่ 1: ตรวจสอบคุณสมบัติพื้นฐานของโซน --- if(zone == NULL || zone.zone_type != ZONE_FVG || !zone.is_significant || zone.state == STATE_INVALIDATED) { continue; } // --- เริ่มการดีบักสำหรับโซนที่น่าสนใจ --- PrintFormat("--- [Fresh FVG Check] Grading zone at %s...", TimeToString(zone.time)); // ด่าน 1.1: เช็ค Blacklist CTradedLevel* level_info = m_zone_manager_ptr.GetTradedLevelInfo(zone.time); if(level_info != NULL && level_info.trade_count >= m_max_trades_per_zone) { Print(" - REJECTED: Already traded max times for this level."); continue; } // ด่าน 1.2: เช็คครั้งที่ของการรีเทส (Nth Retest) // ถ้า m_breakout_retest_number = 1, เราจะมองหาโซนที่ mitigation_count = 0 if(m_breakout_retest_number > 0 && zone.mitigation_count != (m_breakout_retest_number - 1)) { PrintFormat(" - REJECTED: Incorrect mitigation count. Is: %d, Required: %d", zone.mitigation_count, m_breakout_retest_number - 1); continue; } PrintFormat(" - PASSED: Initial checks (Significant, FVG, Not traded, Correct mitigation count)."); // --- ด่านที่ 2: ตรวจสอบการยืนยันการออกจากพื้นที่ (Move Away Confirmation) --- bool confirmation_passed = false; PrintFormat(" - Checking Move Away Confirmation (Mode: %s)...", EnumToString(m_retest_confirmation_mode)); PrintFormat(" - Zone state: Has Displacement? %s, Has Structure Confirmation? %s", (string)zone.has_displacement, (string)zone.has_structure_confirmation); switch(m_retest_confirmation_mode) { case CONF_MODE_NONE: confirmation_passed = true; break; case CONF_MODE_DISPLACEMENT_ONLY: confirmation_passed = zone.has_displacement; break; case CONF_MODE_STRUCTURE_ONLY: confirmation_passed = zone.has_structure_confirmation; break; case CONF_MODE_DISP_AND_STRUCT: confirmation_passed = (zone.has_displacement && zone.has_structure_confirmation); break; } if(!confirmation_passed) { Print(" - REJECTED: Failed Move Away Confirmation check."); continue; } Print(" - PASSED: Move Away Confirmation."); // --- ด่านที่ 3: ตรวจสอบการรีเทส (The Actual Retest Trigger) --- MqlRates last_bar = context.rates[1]; bool is_touching_now = false; if(zone.direction == DIRECTION_BULLISH && last_bar.low <= zone.top) is_touching_now = true; else if (zone.direction == DIRECTION_BEARISH && last_bar.high >= zone.bottom) is_touching_now = true; if(!is_touching_now) { // ไม่ใช่บั๊ก แค่ราคายังไม่มาถึง ไม่ต้อง Print ก็ได้เพื่อไม่ให้ Log รก continue; } Print(" - PASSED: Price is currently retesting the zone."); // --- ด่านที่ 4 (สุดท้าย): ตรวจสอบแท่งเทียนยืนยันอย่างง่าย --- bool candle_confirmed = false; if((zone.direction == DIRECTION_BULLISH && last_bar.close > last_bar.open) || (zone.direction == DIRECTION_BEARISH && last_bar.close < last_bar.open)) { candle_confirmed = true; } if(!candle_confirmed) { Print(" - REJECTED: Retest candle was the wrong color."); continue; } Print(" - PASSED: Retest candle has correct confirmation color."); // --- ถ้าผ่านทุกด่าน --- Print(">>> SUCCESS! Fresh FVG Retest Signal Found. <<<"); zone.trigger_level_time = zone.time; return zone; } return NULL; } // --- Strategy 1: Retest Entry (Final version with full context awareness) --- CSignalZone* CheckForRetestSignal(CDataContext &context) { // ดึงข้อมูลที่จำเป็นมาเตรียมไว้ CArrayObj* zone_list = m_zone_manager_ptr.GetZoneList(); if(zone_list == NULL) return NULL; ENUM_MARKET_STRUCTURE_STATE structure_state = m_analyzer_ptr.GetMarketStructureState(); for(int i = 0; i < zone_list.Total(); i++) { CSignalZone* zone = zone_list.At(i); // --- 1. ด่านกรองเบื้องต้น --- if(zone == NULL || zone.state == STATE_INVALIDATED) continue; CTradedLevel* level_info = m_zone_manager_ptr.GetTradedLevelInfo(zone.time); if(level_info != NULL && level_info.trade_count >= m_max_trades_per_zone) continue; // --- 2. แยกการพิจารณาสำหรับ BUY และ SELL --- // --- ส่วน A: ตรวจสอบสัญญาณ BUY ที่โซน Demand --- if (zone.direction == DIRECTION_BULLISH) { // 2.1 ตรวจสอบว่า "เรื่องราว" ของตลาดเอื้ออำนวยให้ Buy หรือไม่ bool is_valid_buy_narrative = false; // เรื่องราวที่ 1: เทรดตามเทรนด์ขาขึ้น if (m_use_continuation_retest && structure_state == MS_BULLISH) { is_valid_buy_narrative = true; } // เรื่องราวที่ 2: เทรดจังหวะกลับตัวเป็นขาขึ้น if (m_use_reversal_retest && structure_state == MS_BULLISH_CHoCH) { is_valid_buy_narrative = true; } // 2.2 ถ้าเรื่องราวถูกต้อง ค่อยมองหารูปแบบแท่งเทียน if(is_valid_buy_narrative) { /*if(m_use_simple_reclaim_pattern && IsSimpleReclaimPattern(context, zone)) { zone.trigger_level_time = zone.time; return zone; }*/ if(m_use_2_bar_confirmation && Is2BarPowerCandle(context, zone)) { zone.trigger_level_time = zone.time; return zone; } if(m_use_3_bar_confirmation && Is3BarReversal(context, zone)) { zone.trigger_level_time = zone.time; return zone; } } } // --- ส่วน B: ตรวจสอบสัญญาณ SELL ที่โซน Supply --- else if (zone.direction == DIRECTION_BEARISH) { // 2.1 ตรวจสอบว่า "เรื่องราว" ของตลาดเอื้ออำนวยให้ Sell หรือไม่ bool is_valid_sell_narrative = false; // เรื่องราวที่ 1: เทรดตามเทรนด์ขาลง if (m_use_continuation_retest && structure_state == MS_BEARISH) { is_valid_sell_narrative = true; } // เรื่องราวที่ 2: เทรดจังหวะกลับตัวเป็นขาลง if (m_use_reversal_retest && structure_state == MS_BEARISH_CHoCH) { is_valid_sell_narrative = true; } // 2.2 ถ้าเรื่องราวถูกต้อง ค่อยมองหารูปแบบแท่งเทียน if(is_valid_sell_narrative) { /*if(m_use_simple_reclaim_pattern && IsSimpleReclaimPattern(context, zone)) { zone.trigger_level_time = zone.time; return zone; }*/ if(m_use_2_bar_confirmation && Is2BarPowerCandle(context, zone)) { zone.trigger_level_time = zone.time; return zone; } if(m_use_3_bar_confirmation && Is3BarReversal(context, zone)) { zone.trigger_level_time = zone.time; return zone; } } } } return NULL; } CSignalZone* CheckForBreakoutSignal(CDataContext &context, long magic_number) { ENUM_MARKET_STRUCTURE_STATE structure_state = m_analyzer_ptr.GetMarketStructureState(); CArrayObj* major_swings = m_zone_manager_ptr.GetMajorSwings(); if(major_swings == NULL || major_swings.Total() == 0) return NULL; // --- Bullish Breakout --- if(structure_state == MS_BULLISH) { SwingPoint* last_major_high = NULL; for(int i = major_swings.Total() - 1; i >= 0; i--) { SwingPoint* s = major_swings.At(i); if(s != NULL && s.type == SWING_HIGH) { last_major_high = s; break; } } if(last_major_high != NULL) { CTradedLevel* level_info = m_zone_manager_ptr.GetTradedLevelInfo(last_major_high.time); if(level_info == NULL || level_info.trade_count < m_max_trades_per_zone) { if(context.rates[1].close > last_major_high.price && context.rates[1].close > context.rates[1].open) { // --- ส่วนที่เพิ่มเข้ามา --- PrintFormat("BREAKOUT CONFIRMED. Creating new BULLISH BREAKOUT-RETEST ZONE at %.5f", last_major_high.price); CSignalZone* breakout_retest_zone = new CSignalZone(magic_number); breakout_retest_zone.time = last_major_high.time; breakout_retest_zone.top = last_major_high.price; breakout_retest_zone.bottom = last_major_high.price - (context.atr_buffer[1] * 0.2); breakout_retest_zone.zone_type = ZONE_BREAKOUT_RETEST; // *** SET NEW TYPE *** breakout_retest_zone.direction = DIRECTION_BULLISH; breakout_retest_zone.origin_swing_ptr = last_major_high; m_zone_manager_ptr.AddZone(breakout_retest_zone); PrintFormat("BREAKOUT CONFIRMED. Creating new BEARISH FLIP ZONE at %.5f", last_major_high.price); // --- จบส่วนที่เพิ่ม --- // สร้างสัญญาณสำหรับเข้าเทรดไม้แรก if(!m_use_breakout_entry) return NULL; CSignalZone* breakout_signal = new CSignalZone(magic_number); breakout_signal.direction = DIRECTION_BULLISH; breakout_signal.trigger_level_time = last_major_high.time; breakout_signal.top = context.rates[1].high; breakout_signal.bottom = context.rates[1].low; return breakout_signal; } } } } // --- Bearish Breakout --- else if(structure_state == MS_BEARISH) { SwingPoint* last_major_low = NULL; for(int i = major_swings.Total() - 1; i >= 0; i--) { SwingPoint* s = major_swings.At(i); if(s != NULL && s.type == SWING_LOW) { last_major_low = s; break; } } if(last_major_low != NULL) { if(context.rates[1].close < last_major_low.price && context.rates[1].close < context.rates[1].open && context.rates[1].high > last_major_low.price) { // --- ส่วนที่เพิ่มเข้ามา --- PrintFormat("BREAKOUT CONFIRMED. Creating new BEARISH BREAKOUT-RETEST ZONE at %.5f", last_major_low.price); CSignalZone* breakout_retest_zone = new CSignalZone(magic_number); breakout_retest_zone.time = last_major_low.time; breakout_retest_zone.bottom = last_major_low.price; breakout_retest_zone.top = last_major_low.price + (context.atr_buffer[1] * 0.2); breakout_retest_zone.zone_type = ZONE_BREAKOUT_RETEST; // *** SET NEW TYPE *** breakout_retest_zone.direction = DIRECTION_BEARISH; breakout_retest_zone.origin_swing_ptr = last_major_low; m_zone_manager_ptr.AddZone(breakout_retest_zone); PrintFormat("BREAKOUT CONFIRMED. Creating new BEARISH FLIP ZONE at %.5f", last_major_low.price); // --- จบส่วนที่เพิ่ม --- // สร้างสัญญาณสำหรับเข้าเทรดไม้แรก if(!m_use_breakout_entry) return NULL; CSignalZone* breakout_signal = new CSignalZone(magic_number); breakout_signal.direction = DIRECTION_BEARISH; breakout_signal.trigger_level_time = last_major_low.time; breakout_signal.top = context.rates[1].high; breakout_signal.bottom = context.rates[1].low; return breakout_signal; } } } return NULL; } // --- Strategy: Scale-In on Breakout-Retest Zone (UPGRADED) --- CSignalZone* CheckForRetestAfterBreakout(CDataContext &context) { CArrayObj* zone_list = m_zone_manager_ptr.GetZoneList(); if(zone_list == NULL) return NULL; for(int i = 0; i < zone_list.Total(); i++) { CSignalZone* zone = zone_list.At(i); // --- ด่านกรองเบื้องต้น --- if(zone == NULL || zone.zone_type != ZONE_BREAKOUT_RETEST || zone.state == STATE_INVALIDATED) { continue; } CTradedLevel* level_info = m_zone_manager_ptr.GetTradedLevelInfo(zone.time); if(level_info != NULL && level_info.trade_count >= m_max_trades_per_zone) { continue; } // --- ถ้าผ่านทุกด่าน ให้มองหารูปแบบแท่งเทียนยืนยัน --- if(m_use_simple_reclaim_pattern && IsSimpleReclaimPattern(context, zone)) { if(m_breakout_retest_number > 0 && zone.mitigation_count != (m_breakout_retest_number - 1)) { continue; } zone.trigger_level_time = zone.time; return zone; } if(m_use_2_bar_confirmation && Is2BarPowerCandle(context, zone)) { if(m_breakout_retest_number > 0 && zone.mitigation_count != (m_breakout_retest_number - 1)) { continue; } zone.trigger_level_time = zone.time; return zone; } if(m_use_3_bar_confirmation && Is3BarReversal(context, zone)) { if(m_breakout_retest_number > 0 && zone.mitigation_count != (m_breakout_retest_number - 1)) { continue; } zone.trigger_level_time = zone.time; return zone; } } return NULL; } // --- Strategy: Fakeout / Major Swing Failure Pattern Entry (UPGRADED with Structure Confluence) --- CSignalZone* CheckForFakeoutSignal(CDataContext &context, long magic_number) { if(ArraySize(context.rates) < 4) return NULL; CArrayObj* major_swings = m_zone_manager_ptr.GetMajorSwings(); if(major_swings == NULL || major_swings.Total() < 2) return NULL; // ต้องการ Major Swing อย่างน้อย 2 ตัวในแต่ละฝั่งเพื่อเปรียบเทียบ // --- A. ตรวจสอบการเกิด Fakeout ที่ Major Swing High (เพื่อหาจังหวะ SELL) --- SwingPoint* H1 = NULL; // Major High ล่าสุด SwingPoint* H2 = NULL; // Major High ก่อนหน้า int highs_found = 0; for(int i = major_swings.Total() - 1; i >= 0; i--) { SwingPoint* s = major_swings.At(i); if(s != NULL && s.type == SWING_HIGH) { if(highs_found == 0) H1 = s; else if(highs_found == 1) H2 = s; highs_found++; if(highs_found >= 2) break; } } // --- B. ตรวจสอบการเกิด Fakeout ที่ Major Swing Low (เพื่อหาจังหวะ BUY) --- SwingPoint* L1 = NULL; // Major Low ล่าสุด SwingPoint* L2 = NULL; // Major Low ก่อนหน้า int lows_found = 0; for(int i = major_swings.Total() - 1; i >= 0; i--) { SwingPoint* s = major_swings.At(i); if(s != NULL && s.type == SWING_LOW) { if(lows_found == 0) L1 = s; else if(lows_found == 1) L2 = s; lows_found++; if(lows_found >= 2) break; } } if(H1 != NULL && H2 != NULL && L1 != NULL && L2 != NULL) { // +++ NEW: Market Structure Confirmation Filter +++ // ต้องเป็นโครงสร้างขาลง (Lower Highs) เท่านั้น if(H1.price < H2.price && L1.price < L2.price) { CTradedLevel* level_info = m_zone_manager_ptr.GetTradedLevelInfo(H1.time); if(level_info == NULL || level_info.trade_count < m_max_trades_per_zone) { bool is_fakeout_candle = (context.rates[1].high > H1.price && context.rates[1].close < H1.price); if(is_fakeout_candle) { PrintFormat("--- FAKEOUT SETUP ARMED: Bearish Fakeout of Major High at %s. Checking for confirmation...", TimeToString(H1.time)); // 3. ถ้าใช่ ให้สร้าง "โซนเสมือน" ขึ้นมาเพื่อใช้ตรวจสอบรูปแบบการกลับตัว CSignalZone* virtual_zone = new CSignalZone(magic_number); virtual_zone.direction = DIRECTION_BEARISH; virtual_zone.top = context.rates[2].high +2 ; virtual_zone.bottom = context.rates[2].low -2 ; // 4. ใช้รูปแบบ 3-Bar Reversal ที่แข็งแกร่งเป็นตัวยืนยันสุดท้าย if(m_use_3_bar_confirmation && Is3BarReversal(context, virtual_zone)) { Print(">>> FAKEOUT SIGNAL: Bearish Fakeout confirmed! Creating SELL signal."); virtual_zone.trigger_level_time = H1.time; return virtual_zone; // ส่งสัญญาณเทรด } if(m_use_2_bar_confirmation && Is2BarPowerCandle(context, virtual_zone)) { virtual_zone.trigger_level_time = H1.time; return virtual_zone; } /*if(m_use_simple_reclaim_pattern && IsSimpleReclaimPattern(context, virtual_zone)) { virtual_zone.trigger_level_time = H1.time; return virtual_zone; } */ delete virtual_zone; // ถ้าไม่เข้าเงื่อนไข ให้ลบทิ้ง } } } // +++ NEW: Market Structure Confirmation Filter +++ // ต้องเป็นโครงสร้างขาขึ้น (Higher Lows) เท่านั้น if(L1.price > L2.price && H1.price > H2.price) { CTradedLevel* level_info = m_zone_manager_ptr.GetTradedLevelInfo(L1.time); if(level_info == NULL || level_info.trade_count < m_max_trades_per_zone) { bool is_fakeout_candle = (context.rates[1].low < L1.price && context.rates[1].close > L1.price); if(is_fakeout_candle) { PrintFormat("--- FAKEOUT SETUP ARMED: Bullish Fakeout of Major Low at %s. Checking for confirmation...", TimeToString(L1.time)); CSignalZone* virtual_zone = new CSignalZone(magic_number); virtual_zone.direction = DIRECTION_BULLISH; virtual_zone.top = context.rates[2].high + 2; virtual_zone.bottom = context.rates[2].low -2 ; if(m_use_3_bar_confirmation && Is3BarReversal(context, virtual_zone)) { Print(">>> FAKEOUT SIGNAL: Bullish Fakeout confirmed! Creating BUY signal."); virtual_zone.trigger_level_time = L1.time; return virtual_zone; } if(m_use_2_bar_confirmation && Is2BarPowerCandle(context, virtual_zone)) { virtual_zone.trigger_level_time = L1.time; return virtual_zone; } /*if(m_use_simple_reclaim_pattern && IsSimpleReclaimPattern(context, virtual_zone)) { virtual_zone.trigger_level_time = L1.time; return virtual_zone; } */ delete virtual_zone; } } } } return NULL; } CSignalZone* CheckForMajorSwingSignal(CDataContext &context, long magic_number) { CArrayObj* major_swings = m_zone_manager_ptr.GetMajorSwings(); if(major_swings == NULL || major_swings.Total() == 0) return NULL; SwingPoint* last_major_swing = major_swings.At(major_swings.Total() - 1); if(last_major_swing == NULL) return NULL; CTradedLevel* level_info = m_zone_manager_ptr.GetTradedLevelInfo(last_major_swing.time); if(level_info != NULL && level_info.trade_count > 0) return NULL; // เทรดได้แค่ครั้งแรกที่เจอ CSignalZone* momentum_signal = NULL; if(last_major_swing.type == SWING_LOW) { momentum_signal = new CSignalZone(magic_number); momentum_signal.direction = DIRECTION_BULLISH; momentum_signal.trigger_level_time = last_major_swing.time; int bar_idx = iBarShift(_Symbol, PERIOD_CURRENT, last_major_swing.time); if(bar_idx >= 0) { momentum_signal.bottom = iLow(_Symbol, PERIOD_CURRENT, bar_idx); momentum_signal.top = iHigh(_Symbol, PERIOD_CURRENT, bar_idx); } } else if(last_major_swing.type == SWING_HIGH) { momentum_signal = new CSignalZone(magic_number); momentum_signal.direction = DIRECTION_BEARISH; momentum_signal.trigger_level_time = last_major_swing.time; int bar_idx = iBarShift(_Symbol, PERIOD_CURRENT, last_major_swing.time); if(bar_idx >= 0) { momentum_signal.top = iHigh(_Symbol, PERIOD_CURRENT, bar_idx); momentum_signal.bottom = iLow(_Symbol, PERIOD_CURRENT, bar_idx); } } return momentum_signal; } // --- Strategy: Swing Failure Pattern (SFP) Entry --- CSignalZone* CheckForSfpSignal(CDataContext &context, long magic_number) { if(ArraySize(context.rates) < 3) return NULL; CArrayObj* all_swings = m_zone_manager_ptr.GetAllSwings(); if(all_swings == NULL || all_swings.Total() == 0) return NULL; // --- Check for Bullish SFP (to BUY) --- // 1. แท่งยืนยัน (rates[1]) ต้องเป็นแท่งเขียว if(context.rates[1].close > context.rates[1].open) { // 2. หา Swing Low ก่อนหน้าแท่งที่อาจจะเป็น SFP (rates[2]) SwingPoint* prev_low = GetPreviousSwing(2, SWING_LOW, all_swings); if(prev_low != NULL) { // 3. เช็คว่าแท่งที่ 2 เป็นแท่ง SFP จริงหรือไม่ (ไส้ทะลุ, เนื้อปิดกลับ) bool is_sfp_candle = (context.rates[2].low < prev_low.price) && (context.rates[2].close > prev_low.price); if(is_sfp_candle) { // 4. เช็คบัญชีดำ CTradedLevel* level_info = m_zone_manager_ptr.GetTradedLevelInfo(prev_low.time); if(level_info == NULL || level_info.trade_count < m_max_trades_per_zone) { Print("--- SFP SIGNAL: Bullish SFP Detected! ---"); CSignalZone* sfp_signal = new CSignalZone(magic_number); sfp_signal.direction = DIRECTION_BULLISH; sfp_signal.trigger_level_time = prev_low.time; sfp_signal.bottom = context.rates[2].low; // SL อิงจาก Low ของแท่ง SFP sfp_signal.top = context.rates[2].high; return sfp_signal; } } } } // --- Check for Bearish SFP (to SELL) --- // 1. แท่งยืนยัน (rates[1]) ต้องเป็นแท่งแดง if(context.rates[1].close < context.rates[1].open) { // 2. หา Swing High ก่อนหน้าแท่ง SFP SwingPoint* prev_high = GetPreviousSwing(2, SWING_HIGH, all_swings); if(prev_high != NULL) { // 3. เช็คว่าแท่งที่ 2 เป็นแท่ง SFP จริงหรือไม่ bool is_sfp_candle = (context.rates[2].high > prev_high.price) && (context.rates[2].close < prev_high.price); if(is_sfp_candle) { CTradedLevel* level_info = m_zone_manager_ptr.GetTradedLevelInfo(prev_high.time); if(level_info == NULL || level_info.trade_count < m_max_trades_per_zone) { Print("--- SFP SIGNAL: Bearish SFP Detected! ---"); CSignalZone* sfp_signal = new CSignalZone(magic_number); sfp_signal.direction = DIRECTION_BEARISH; sfp_signal.trigger_level_time = prev_high.time; sfp_signal.top = context.rates[2].high; // SL อิงจาก High ของแท่ง SFP sfp_signal.bottom = context.rates[2].low; return sfp_signal; } } } } return NULL; } // --- Strategy: Liquidity Sweep into POI Entry --- CSignalZone* CheckForSweepToPoiSignal(CDataContext &context) { CArrayObj* all_zones = m_zone_manager_ptr.GetZoneList(); CArrayObj* all_swings = m_zone_manager_ptr.GetAllSwings(); if(all_zones == NULL || all_swings == NULL || ArraySize(context.rates) < 2) return NULL; // 1. วนลูปหาโซนคุณภาพสูง (POI) ที่พร้อมเทรด for(int i = 0; i < all_zones.Total(); i++) { CSignalZone* zone = all_zones.At(i); if(zone == NULL || zone.state == STATE_INVALIDATED) continue; CTradedLevel* level_info = m_zone_manager_ptr.GetTradedLevelInfo(zone.time); if(level_info != NULL && level_info.trade_count >= m_max_trades_per_zone) continue; // 2. สำหรับแต่ละโซน, หา Liquidity ที่อยู่ใกล้ๆ (Swing ก่อนหน้า) SwingPoint* liquidity_swing = NULL; int zone_bar_idx = iBarShift(_Symbol, PERIOD_CURRENT, zone.time); if(zone.direction == DIRECTION_BULLISH) { // โซน Demand liquidity_swing = GetPreviousSwing(zone_bar_idx, SWING_LOW, all_swings); } else { // โซน Supply liquidity_swing = GetPreviousSwing(zone_bar_idx, SWING_HIGH, all_swings); } // 3. ตรวจสอบว่ามี Liquidity ให้กวาดและอยู่ในตำแหน่งที่ถูกต้องหรือไม่ if(liquidity_swing != NULL) { bool is_valid_setup = (zone.direction == DIRECTION_BULLISH && liquidity_swing.price > zone.top) || (zone.direction == DIRECTION_BEARISH && liquidity_swing.price < zone.bottom); if(is_valid_setup) { // 4. ตรวจสอบแท่งเทียนล่าสุด (rates[1]) ว่าเกิดเหตุการณ์ "กวาดแล้วแตะ" หรือไม่ MqlRates last_bar = context.rates[1]; bool swept_and_tapped = false; if(zone.direction == DIRECTION_BULLISH && last_bar.low < liquidity_swing.price && last_bar.low <= zone.top) { swept_and_tapped = true; } else if(zone.direction == DIRECTION_BEARISH && last_bar.high > liquidity_swing.price && last_bar.high >= zone.bottom) { swept_and_tapped = true; } // 5. ถ้าใช่, ให้มองหารูปแบบแท่งเทียนยืนยันเพื่อเข้าเทรด if(swept_and_tapped) { PrintFormat("--- SWEEP+POI SETUP ARMED at zone %s, checking for confirmation...", TimeToString(zone.time)); if(m_use_simple_reclaim_pattern && IsSimpleReclaimPattern(context, zone)) { zone.trigger_level_time = zone.time; return zone; } if(m_use_2_bar_confirmation && Is2BarPowerCandle(context, zone)) { zone.trigger_level_time = zone.time; return zone; } if(m_use_3_bar_confirmation && Is3BarReversal(context, zone)) { zone.trigger_level_time = zone.time; return zone; } } } } } return NULL; } public: CEntryExecutor(CZoneManager* zone_manager, CStructureAnalyzer* analyzer, int max_trades, bool use_continuation, bool use_reversal, bool use_breakout, bool use_fakeout, bool use_major_swing, bool use_sfp, bool use_sweep_poi, bool use_scale_in, int retest_num, bool use_fresh_fvg, bool use_simple, bool use_2_bar, bool use_3_bar, bool use_sto_confirm, int sto_ob, int sto_os, ENUM_RETEST_CONFIRMATION_MODE retest_mode, double retest_atr_mult) { m_zone_manager_ptr = zone_manager; m_analyzer_ptr = analyzer; m_max_trades_per_zone = max_trades; m_use_continuation_retest = use_continuation; m_use_reversal_retest = use_reversal; m_use_breakout_entry = use_breakout; m_use_fakeout_entry = use_fakeout; m_use_major_swing_momentum_entry = use_major_swing; m_use_sfp_entry = use_sfp; m_use_sweep_and_poi_entry = use_sweep_poi; m_use_scale_in_on_breakout = use_scale_in; m_breakout_retest_number = retest_num; m_use_fresh_fvg_retest_entry = use_fresh_fvg; m_use_simple_reclaim_pattern = use_simple; m_use_2_bar_confirmation = use_2_bar; m_use_3_bar_confirmation = use_3_bar; m_use_sto_for_entry_confirmation = use_sto_confirm; m_sto_overbought = sto_ob; m_sto_oversold = sto_os; m_retest_confirmation_mode = retest_mode; m_retest_min_atr_moveaway = retest_atr_mult; } ~CEntryExecutor() {} // --- The New, Unified Master "Brain" --- CSignalZone* FindTradeSignal(CDataContext &context, long magic_number) { CSignalZone* signal = NULL; // ไล่ตรวจสอบแต่ละท่าเทรดตามลำดับความสำคัญหรือความถี่ที่ต้องการ if(m_use_fresh_fvg_retest_entry) { signal = CheckForFreshFvgRetest(context); if(signal != NULL) return signal; } if(m_use_continuation_retest || m_use_reversal_retest) { signal = CheckForRetestSignal(context); if(signal != NULL) return signal; } if(m_use_sweep_and_poi_entry) { signal = CheckForSweepToPoiSignal(context); if(signal != NULL) return signal; } if(m_use_breakout_entry || m_use_scale_in_on_breakout) { signal = CheckForBreakoutSignal(context, magic_number); if(signal != NULL) return signal; } if(m_use_scale_in_on_breakout) { signal = CheckForRetestAfterBreakout(context); if(signal != NULL) return signal; } if(m_use_fakeout_entry) { signal = CheckForFakeoutSignal(context, magic_number); if(signal != NULL) return signal; } if(m_use_sfp_entry) { signal = CheckForSfpSignal(context, magic_number); if(signal != NULL) return signal; } if(m_use_major_swing_momentum_entry) { signal = CheckForMajorSwingSignal(context, magic_number); if(signal != NULL) return signal; } return NULL; } }; #endif