EA_SMC_Mql5/Include/CEntryExecutor.mqh
2025-07-03 23:36:59 +07:00

855 lines
No EOL
86 KiB
MQL5

//+------------------------------------------------------------------+
//| 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