//+------------------------------------------------------------------+ //| EA SMC.mq5 | //| v23.1 - Final Blueprint Architecture (Complete) | //| Written with 'Coding Buddy' by Google | //+------------------------------------------------------------------+ #property copyright "Your Name" #property link "https://github.com" #property version "23.1" #property description "SMC Expert Advisor with a professional, fully decoupled architecture based on an advanced blueprint." // --- Standard & Custom Library Includes --- #include #include #include #include #include #include #include #include #include #include #include input group "--- Main Exit Strategy ---"; input ENUM_EXIT_MODE InpExitMode = EXIT_MODE_TRAILING_STOP; input double InpRiskToRewardRatio = 0.8; input int InpFixedTPPoints = 1400; input group "--- Trade & Risk Settings ---"; input long InpMagicNumber = 0; input string InpEaComment = "EA_SMC_v23.1"; input int InpMaxOpenTrades = 2; input int InpStopLossBufferPoints = 50; input ulong InpSlippagePoints = 10; input int InpMinStopLossPoints = 1450; input int InpMaxStopLossPoints = 1450; // เพดาน SL สูงสุดที่รับได้ (0 = ปิดใช้งาน) input group "--- Money Management Mode ---"; input ENUM_MONEY_MANAGEMENT_MODE InpMoneyManagementMode = MM_MODE_LOT_PER_BALANCE; input group "--- Settings for: Fixed Lot ---"; input double InpFixedLotSize = 1; // Lot ที่จะใช้ถ้าเลือก MM_MODE_FIXED_LOT input group "--- Settings for: Risk Percent per Trade ---"; input double InpRiskPercentPerTrade = 4; // % ความเสี่ยงต่อเทรด (ใช้กับ MM_MODE_RISK_PERCENT) input group "--- Settings for: Lot Scaling by Balance ---"; input double InpBaseLotForScaling = 1; // ขนาด Lot พื้นฐาน input double InpBalanceStepForScaling = 10000.0; // ทุกๆ Balance ที่เพิ่มขึ้นเท่านี้ จะเพิ่ม Lot เท่ากับ InpBaseLotForScaling input group "--- Entry Strategy Toggles ---"; input int InpMaxTradesPerZone = 1; input bool InpUseContinuationRetest = false; // +++ ADD: ท่า Retest ตามเทรนด์ (BOS) input bool InpUseReversalRetest = false; // +++ ADD: ท่า Retest หลังเกิด CHoCH input bool InpUseBreakoutEntry = true; // เปิด/ปิด ท่า Breakout input bool InpUseScaleInOnBreakout = true; input int InpBreakout_RetestNumber = 1; // 1 = เทรดรีเทสครั้งแรก, 2 = ครั้งที่สอง, 0 = ปิดใช้งาน input bool InpUseFakeoutEntry = false; // เปิด/ปิด ท่า Fakeout (ที่ Major Swing) input bool InpUseMajorSwingMomentumEntry = false; // เปิด/ปิด ท่าเข้าตาม Major Swing input bool InpUseSfpEntry = false; // เปิด/ปิด ท่า SFP (ที่ Minor Swing) input bool InpUseSweepToPoiEntry = false; // เปิด/ปิด ท่า Sweep into POIut) input bool InpUseFreshFvgRetestEntry = true; input group "--- Confirmation Pattern Toggles ---"; input bool InpUseStoForEntryConfirmation = true; input bool InpUseSimpleReclaimPattern = true; input bool InpUse2BarConfirmation = true; // เปิด/ปิด รูปแบบยืนยัน 2 แท่งเทียน input bool InpUse3BarConfirmation = true; // เปิด/ปิด รูปแบบยืนยัน 3 แท่งเทียน //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ input group "--- FVG Retest Settings (Advanced) ---"; input ENUM_RETEST_CONFIRMATION_MODE InpRetest_ConfirmationMode = CONF_MODE_DISP_AND_STRUCT; input double InpRetest_MinAtrMoveAway = 1.0; // --- User-Configurable Inputs --- input group "--- Structure Analysis Settings ---"; input int InpSwingLength = 14; // Length for fractal swing detection input double InpAtrNoiseFilter = 0.25; // Swing must be larger than 0.25 * ATR to be considered input int InpMaxBarsForBOS = 15; // Max bars to look ahead for a BOS after a takeout input ENUM_BOS_TYPE InpBosConfirmationType = BOS_BY_CLOSE; input int InpPdArrayLookbackSwings = 15; input double InpMinFvgAtrMult = 0.3; input ENUM_ZONE_QUALITY_LEVEL InpMinZoneQuality = ZQ_STANDARD; // <<< ตั้งค่าเริ่มต้นให้ "ผ่อนคลาย" ที่สุด input group "--- Advanced Trade Management ---"; input bool InpUsePartialTP = false; input double InpPartialTP_RR = 0.8; input double InpPartialClosePercent = 50.0; input double InpPsarStep = 0.02; input double InpPsarMax = 0.2; input group "--- Dynamic Trailing Stop Settings ---"; input double InpTrail_Tier1_RR = 1.0; // เริ่ม Trail ครั้งแรกเมื่อกำไรถึง 1R input int InpTrail_Tier1_Points = 500; // โดยใช้ระยะตาม 500 points input double InpTrail_Tier2_RR = 2.0; // เมื่อกำไรถึง 2R input int InpTrail_Tier2_Points = 300; // ให้เปลี่ยนระยะตามเป็น 300 points input double InpTrail_Tier3_RR = 3.0; // เมื่อกำไรถึง 3R input int InpTrail_Tier3_Points = 100; // ให้เปลี่ยนระยะตามเป็น 100 points //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ input group "--- Indicator Filters (Advanced) ---"; input bool InpFilterSwingsBySweep = true; input bool InpFilterSwingsByFVG = true; input bool InpUseVolumeFilter = false; input bool InpUseRsiFilter = true; // เปิด/ปิด การใช้ฟิลเตอร์ RSI input int InpRsiPeriod = 14; // ค่า Period ของ RSI input int InpRsiOverbought = 70; // ระดับ Overbought input int InpRsiOversold = 30; // ระดับ Oversold input bool InpUseStochasticFilter = true; // เปิด/ปิด การใช้ฟิลเตอร์ Stochastic input int InpSto_K_Period = 14; // %K Period input int InpSto_D_Period = 3; // %D Period input int InpSto_Slowing = 3; // Slowing input int InpSto_Overbought = 80; // ระดับ Overbought input int InpSto_Oversold = 20; // ระดับ Oversold input group "--- Time Filter Settings ---"; input bool InpUseTimeFilter = false; input int InpSession1_StartHour = 14; input int InpSession1_EndHour = 22; input bool InpUseFridayExit = false; input int InpFridayExit_Hour = 22; input int InpFridayExit_Minute = 0; // --- Global Pointers to All Modules --- CZoneManager* g_zoneManager_ptr = NULL; CChartDrawer* g_drawer_ptr = NULL; CRiskManager* g_riskManager_ptr = NULL; CSetupScanner* g_setupScanner_ptr = NULL; CStructureAnalyzer* g_analyzer_ptr = NULL; CEntryExecutor* g_entryExecutor_ptr = NULL; CTimeFilter* g_timeFilter_ptr = NULL; CTradeManager* g_tradeManager_ptr = NULL; // --- Global Indicator Handles --- int g_atr_handle = INVALID_HANDLE; int g_rsi_handle = INVALID_HANDLE; int g_volume_handle = INVALID_HANDLE; int g_sto_handle = INVALID_HANDLE; // --- Global Timer Variables for OnTick --- static bool is_initial_scan_done = false; static datetime last_sl_tp_check_time = 0; const int SL_TP_CHECK_INTERVAL = 10; // ตรวจสอบทุกๆ 10 วินาที static datetime last_processed_bar_time = 0; //+------------------------------------------------------------------+ int OnInit() { // --- 1. Initial Print & Seeding Randomizer --- Print("EA vFinal (Stateful/Incremental Architecture) Initializing..."); MathSrand(GetTickCount()); // เพื่อให้การสุ่ม ID ของโซนไม่ซ้ำกัน // --- 2. สร้าง instance ของโมดูลทั้งหมด --- g_zoneManager_ptr = new CZoneManager(); g_drawer_ptr = new CChartDrawer(ChartID(), InpMagicNumber); g_riskManager_ptr = new CRiskManager(InpMoneyManagementMode, InpFixedLotSize, InpRiskPercentPerTrade, InpBaseLotForScaling, InpBalanceStepForScaling); g_setupScanner_ptr = new CSetupScanner(g_zoneManager_ptr, InpSwingLength, InpMagicNumber, InpMinFvgAtrMult, InpUseStochasticFilter, InpSto_Overbought, InpSto_Oversold); g_analyzer_ptr = new CStructureAnalyzer(g_zoneManager_ptr, InpAtrNoiseFilter, InpMaxBarsForBOS, InpBosConfirmationType, InpPdArrayLookbackSwings, InpUseRsiFilter, InpRsiPeriod,InpRsiOverbought, InpRsiOversold, InpFilterSwingsByFVG, InpFilterSwingsBySweep,InpRetest_MinAtrMoveAway,InpMinZoneQuality); g_entryExecutor_ptr = new CEntryExecutor(g_zoneManager_ptr, g_analyzer_ptr, InpMaxTradesPerZone, InpUseContinuationRetest, InpUseReversalRetest,InpUseBreakoutEntry, InpUseFakeoutEntry, InpUseMajorSwingMomentumEntry, InpUseSfpEntry, InpUseSweepToPoiEntry, InpUseScaleInOnBreakout, InpBreakout_RetestNumber, InpUseFreshFvgRetestEntry, InpUseSimpleReclaimPattern, InpUse2BarConfirmation, InpUse3BarConfirmation, InpUseStoForEntryConfirmation, InpSto_Overbought, InpSto_Oversold, InpRetest_ConfirmationMode, InpRetest_MinAtrMoveAway); g_timeFilter_ptr = new CTimeFilter(InpUseTimeFilter, InpSession1_StartHour, InpSession1_EndHour, 0, 0, 0, 0, false, 0, 0, 0, 0, InpUseFridayExit, InpFridayExit_Hour, InpFridayExit_Minute, ChartID(), InpMagicNumber); g_tradeManager_ptr = new CTradeManager(g_riskManager_ptr, InpMagicNumber, InpEaComment, InpSlippagePoints, InpStopLossBufferPoints, InpMinStopLossPoints, InpMaxStopLossPoints, InpExitMode, InpRiskToRewardRatio, InpFixedTPPoints, InpTrail_Tier1_RR, InpTrail_Tier1_Points, InpTrail_Tier2_RR, InpTrail_Tier2_Points, InpTrail_Tier3_RR, InpTrail_Tier3_Points, InpPsarStep, InpPsarMax, InpUsePartialTP, InpPartialTP_RR, InpPartialClosePercent); // --- 3. Pointer Validation --- if(CheckPointer(g_zoneManager_ptr)==POINTER_INVALID || CheckPointer(g_drawer_ptr)==POINTER_INVALID || CheckPointer(g_riskManager_ptr)==POINTER_INVALID || CheckPointer(g_setupScanner_ptr)==POINTER_INVALID || CheckPointer(g_analyzer_ptr)==POINTER_INVALID || CheckPointer(g_entryExecutor_ptr)==POINTER_INVALID || CheckPointer(g_timeFilter_ptr)==POINTER_INVALID || CheckPointer(g_tradeManager_ptr)==POINTER_INVALID) { Print("FATAL: A core module failed to initialize."); return(INIT_FAILED); } // --- 4. สร้าง Indicator Handles --- g_atr_handle = iATR(_Symbol, PERIOD_CURRENT, 14); if(g_atr_handle == INVALID_HANDLE) { Print("Failed to create ATR Handle!"); return(INIT_FAILED); } if(InpUseRsiFilter) { g_rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, InpRsiPeriod, PRICE_CLOSE); if(g_rsi_handle == INVALID_HANDLE) { Print("Failed to create RSI Handle!"); return(INIT_FAILED); } } if(InpUseVolumeFilter) { g_volume_handle = iVolume(_Symbol, PERIOD_CURRENT, VOLUME_TICK); if(g_volume_handle == INVALID_HANDLE) { Print("Failed to create Volume Handle!"); return(INIT_FAILED); } } if(InpUseStochasticFilter) { g_sto_handle = iStochastic(_Symbol, PERIOD_CURRENT, InpSto_K_Period, InpSto_D_Period, InpSto_Slowing, MODE_SMA, STO_LOWHIGH); if(g_sto_handle == INVALID_HANDLE) { Print("Failed to create Stochastic Handle!"); return(INIT_FAILED); } } Print("--- Initialization Complete. Waiting for first tick to perform historical analysis... ---"); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(CheckPointer(g_tradeManager_ptr)) delete g_tradeManager_ptr; if(CheckPointer(g_timeFilter_ptr)) delete g_timeFilter_ptr; if(CheckPointer(g_entryExecutor_ptr)) delete g_entryExecutor_ptr; if(CheckPointer(g_analyzer_ptr)) delete g_analyzer_ptr; if(CheckPointer(g_setupScanner_ptr)) delete g_setupScanner_ptr; if(CheckPointer(g_riskManager_ptr)) delete g_riskManager_ptr; if(CheckPointer(g_drawer_ptr)) delete g_drawer_ptr; if(CheckPointer(g_zoneManager_ptr)) delete g_zoneManager_ptr; if(g_atr_handle != INVALID_HANDLE) IndicatorRelease(g_atr_handle); Comment(""); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // ================================================================== // เฟสที่ 1: การวิเคราะห์ย้อนหลังครั้งแรก (ทำงานแค่ครั้งเดียวตอนเริ่มต้น) // ================================================================== if(!is_initial_scan_done) { int history_bars = 500; if(Bars(_Symbol, PERIOD_CURRENT) < history_bars + 200) { return; } // --- The Patient Priming Loop (Corrected) --- bool all_indicators_ready = false; Print("--- Priming Indicator Data... Will wait up to 30 seconds. ---"); for(int i = 0; i < 300; i++) { int atr_calc = BarsCalculated(g_atr_handle); int rsi_calc = !InpUseRsiFilter ? history_bars : BarsCalculated(g_rsi_handle); int vol_calc = !InpUseVolumeFilter ? history_bars : BarsCalculated(g_volume_handle); int sto_calc = !InpUseStochasticFilter ? history_bars : BarsCalculated(g_sto_handle); // --- REMOVED: ไม่มีการเช็ค MA ที่ถูกลบไปแล้ว --- PrintFormat("Priming Attempt #%d: ATR(%d/%d), RSI(%d/%d), VOL(%d/%d), STO(%d/%d)", i+1, atr_calc, history_bars, rsi_calc, history_bars, vol_calc, history_bars, sto_calc, history_bars); if(atr_calc >= history_bars && rsi_calc >= history_bars && vol_calc >= history_bars && sto_calc >= history_bars) { all_indicators_ready = true; Print("--- All indicator data is ready. ---"); break; } Sleep(100); } if(!all_indicators_ready) { Print("FATAL: Failed to prepare indicator data within 30 seconds. Stopping EA."); ExpertRemove(); return; } // --- เริ่มการวิเคราะห์ครั้งใหญ่ --- Print("--- Performing Initial Historical Analysis on %d bars... ---", history_bars); CDataContext initial_context(history_bars); if(CopyRates(_Symbol, PERIOD_CURRENT, 0, history_bars, initial_context.rates) < history_bars) return; if(CopyBuffer(g_atr_handle, 0, 0, history_bars, initial_context.atr_buffer) < history_bars) return; if(InpUseRsiFilter) if(CopyBuffer(g_rsi_handle, 0, 0, history_bars, initial_context.rsi_buffer) < history_bars) return; if(InpUseVolumeFilter) if(CopyBuffer(g_volume_handle, 0, 0, history_bars, initial_context.volume_buffer) < history_bars) return; if(InpUseStochasticFilter) if(CopyBuffer(g_sto_handle, 0, 0, history_bars, initial_context.sto_main_buffer) < history_bars || CopyBuffer(g_sto_handle, 1, 0, history_bars, initial_context.sto_signal_buffer) < history_bars) return; // --- REMOVED: ไม่มีการโหลด Buffer ของ MA ที่ถูกลบไปแล้ว --- g_setupScanner_ptr.ScanHistory(initial_context); g_analyzer_ptr.Analyze(initial_context); is_initial_scan_done = true; last_processed_bar_time = initial_context.rates[0].time; Print("--- Initial Analysis Complete. EA is now live. ---"); ChartRedraw(); return; } // ================================================================== // เฟสที่ 2: การทำงานปกติในแต่ละ Tick (หลังจากวิเคราะห์ครั้งแรกเสร็จแล้ว) // ================================================================== // --- 2.1 งานที่ต้องทำทุก Tick (Urgent) --- if(CheckPointer(g_tradeManager_ptr)) { g_tradeManager_ptr.ManageActiveTrailing(); } // --- 2.2 งานที่ทำตามเวลา (Scheduled) --- if(TimeCurrent() - last_sl_tp_check_time >= SL_TP_CHECK_INTERVAL) { if(CheckPointer(g_tradeManager_ptr)) g_tradeManager_ptr.ManageUnsetSLTP(); last_sl_tp_check_time = TimeCurrent(); } // --- 2.3 ตรวจจับและทำงานเมื่อเกิดแท่งเทียนใหม่ --- datetime current_bar_time = (datetime)SeriesInfoInteger(_Symbol, PERIOD_CURRENT, SERIES_LASTBAR_DATE); if(current_bar_time == last_processed_bar_time) return; int new_bars_count = (int)((current_bar_time - last_processed_bar_time) / PeriodSeconds(PERIOD_CURRENT)); last_processed_bar_time = current_bar_time; if(new_bars_count <= 0) return; // --- เริ่มกระบวนการทำงานของแท่งเทียนใหม่ --- CDataContext context(500); // ... (โค้ดโหลดข้อมูล 500 แท่งใส่ context เหมือนใน OnInit เดิม) ... for(int i = 0; i < context.bars_to_load; i++) { context.rates[i].time = iTime(_Symbol, PERIOD_CURRENT, i); context.rates[i].open = iOpen(_Symbol, PERIOD_CURRENT, i); context.rates[i].high = iHigh(_Symbol, PERIOD_CURRENT, i); context.rates[i].low = iLow(_Symbol, PERIOD_CURRENT, i); context.rates[i].close = iClose(_Symbol, PERIOD_CURRENT, i); } // โหลดข้อมูล Indicators if(CopyBuffer(g_atr_handle, 0, 0, context.bars_to_load, context.atr_buffer) < context.bars_to_load) return; if(InpUseRsiFilter && CopyBuffer(g_rsi_handle, 0, 0, context.bars_to_load, context.rsi_buffer) < 500) return; if(InpUseVolumeFilter && CopyBuffer(g_volume_handle, 0, 0, context.bars_to_load, context.volume_buffer) < 500) return; // +++ ADD THIS BLOCK TO LOAD STOCHASTIC DATA +++ if(InpUseStochasticFilter && g_sto_handle != INVALID_HANDLE) { if(CopyBuffer(g_sto_handle, 0, 0, context.bars_to_load, context.sto_main_buffer) < 500 || CopyBuffer(g_sto_handle, 1, 0, context.bars_to_load, context.sto_signal_buffer) < 500) { Print("Failed to copy Sto buffer in OnTick. Skipping this tick."); return; } } // --- 2.4 สายการผลิตแบบ Incremental --- if(CheckPointer(g_setupScanner_ptr)) g_setupScanner_ptr.ScanIncremental(context, new_bars_count); if(CheckPointer(g_analyzer_ptr)) g_analyzer_ptr.Analyze(context); if(CheckPointer(g_tradeManager_ptr) && InpExitMode == EXIT_MODE_STRUCTURAL) { g_tradeManager_ptr.ManageStructuralExits(g_zoneManager_ptr.GetAllSwings()); } if(CheckPointer(g_entryExecutor_ptr) && (int)PositionsTotal() < InpMaxOpenTrades) { CSignalZone* trade_signal = g_entryExecutor_ptr.FindTradeSignal(context, InpMagicNumber); if(trade_signal != NULL) { if(g_tradeManager_ptr.OpenTradeFromSignal(trade_signal)) { if(trade_signal.trigger_level_time > 0) g_zoneManager_ptr.IncrementTradeCountForLevel(trade_signal.trigger_level_time); } if(trade_signal.zone_id == 0) delete trade_signal; } } // --- 2.5 วาดผล --- if(CheckPointer(g_drawer_ptr) && CheckPointer(g_zoneManager_ptr)) { CArrayObj* all_zones = g_zoneManager_ptr.GetZoneList(); // ใช้ all_zones เพื่อวาดสถานะ is_significant CArrayObj* all_swings = g_zoneManager_ptr.GetAllSwings(); g_drawer_ptr.SynchronizeDrawings(all_zones, all_swings); // ส่ง all_zones if(all_zones != NULL) { for(int i = 0; i < all_zones.Total(); i++) g_drawer_ptr.DrawZone(all_zones.At(i), context); } if(all_swings != NULL) { for(int i = 0; i < all_swings.Total(); i++) g_drawer_ptr.DrawSwing(all_swings.At(i), all_swings, context); } } ChartRedraw(); } //+------------------------------------------------------------------+