//+------------------------------------------------------------------+ //| OI_MeanReversion_Pro_XAUUSD_V2.mq5 | //| Professional OI Mean Reversion System | //| Version 2.0 | //+------------------------------------------------------------------+ #property copyright "Professional OI Mean Reversion System" #property link "" #property version "2.00" #property description "Advanced Mean Reversion with OI Levels & Multi-Timeframe Filtering" #property strict //--- Includes #include #include #include #include #include #include #include #include //--- Market Phase Enum (Moved to global scope) enum ENUM_MARKET_PHASE { PHASE_BULLISH, PHASE_BEARISH, PHASE_SIDEWAYS, PHASE_NEUTRAL }; //--- Input Parameters (จัดกลุ่มตามสเปก) // Group A: OI & Delta Configuration input group "=== OI & DELTA SETTINGS ===" enum ENUM_OI_SOURCE { OI_SOURCE_MANUAL, // Manual Input OI_SOURCE_CSV_FILE, // CSV File OI_SOURCE_AUTO_SYNC // Auto Sync (Future) }; input ENUM_OI_SOURCE InpOISource = OI_SOURCE_CSV_FILE; input string InpOICsvPath = "oi_data.csv"; // Path to CSV file input int InpCSVReloadInterval = 60; // CSV reload interval (minutes, 0=disabled) input double InpManualFuturePrice = 0.0; // Manual future price input // OI Levels (Manual Input - สามารถใส่ได้ถึง 3 ระดับ) input double InpCallStrike1 = 0.0; input double InpCallStrike2 = 0.0; input double InpCallStrike3 = 0.0; input double InpPutStrike1 = 0.0; input double InpPutStrike2 = 0.0; input double InpPutStrike3 = 0.0; // Delta Calculation input int InpDeltaEmaPeriod = 20; // EMA period for Delta average input double InpDeviationThreshold = 20.0; // Deviation threshold in points input double InpDeviationMultiplier = 1.5; // Deviation multiplier for entry // Group B: Trading Configuration input group "=== TRADING SETTINGS ===" input double InpLotSize = 0.1; // Fixed lot size input bool InpUseMoneyManagement = true; // Use money management input double InpRiskPercent = 1.0; // Risk per trade (%) input bool InpUseStopLoss = true; // Use stop loss input bool InpUseTakeProfit = true; // Use take profit input int InpStopLossPoints = 300; // SL in points input int InpTakeProfitPoints = 500; // TP in points input int InpMaxSpread = 200; // Max spread in points input int InpMaxSlippage = 10; // Max slippage in points input int InpMagicNumber = 202412; // Magic number input bool InpCloseOnZeroDeviation = true; // Close when deviation crosses zero // Group C: Market Filters input group "=== MARKET FILTERS ===" input bool InpUseMarketPhaseFilter = true; // Filter by market phase input ENUM_TIMEFRAMES InpTrendTF = PERIOD_H4; // Trend timeframe input int InpTrendMAPeriod1 = 50; // Fast MA period input int InpTrendMAPeriod2 = 200; // Slow MA period input bool InpUseATRFilter = true; // Use ATR volatility filter input double InpMaxATRPercent = 2.0; // Max ATR% for trading input bool InpUseSessionFilter = true; // Filter by trading session input int InpSessionStartHour = 8; // Session start (GMT) input int InpSessionEndHour = 22; // Session end (GMT) input bool InpAvoidHighImpactNews = true; // Avoid high impact news // Group D: Risk Management input group "=== RISK MANAGEMENT ===" input double InpMaxDailyLossPercent = 3.0; // Max daily loss % input double InpMaxDailyProfitPercent = 5.0; // Max daily profit % input int InpMaxConsecutiveLosses = 3; // Max consecutive losses input bool InpDisableAfterMaxLoss = true; // Disable after max loss input double InpEquityProtectionPercent = 10.0; // Equity protection % input bool InpCloseAllOnReverse = false; // Close all on market reversal input int InpMaxDailyTrades = 10; // Maximum trades per day // Group E: Advanced Features input group "=== ADVANCED FEATURES ===" input bool InpUseTrailingStop = false; // Use trailing stop input int InpTrailingStartPoints = 150; // Trailing start in points input int InpTrailingStepPoints = 50; // Trailing step in points input bool InpUseSoftStopLoss = true; // Use soft stop loss (close manually) input bool InpUseAutoRecovery = true; // Auto recover after restart input bool InpEnableDashboard = true; // Enable dashboard display //--- Global Objects CTrade Trade; CSymbolInfo SymbolInfo; CAccountInfo AccountInfo; CPositionInfo PositionInfo; COrderInfo OrderInfo; CHistoryOrderInfo HistoryOrderInfo; //--- Global Variables double DeltaPrice = 0.0; double DeltaEMA = 0.0; double DeltaDeviation = 0.0; double FuturePrice = 0.0; double SpotPrice = 0.0; // OI Levels Arrays double CallLevels[3]; double PutLevels[3]; int CallOI[3]; int PutOI[3]; // Dynamic OI Data Variables (modifiable) double DynamicFuturePrice = 0.0; double DynamicCallStrike1 = 0.0; double DynamicCallStrike2 = 0.0; double DynamicCallStrike3 = 0.0; double DynamicPutStrike1 = 0.0; double DynamicPutStrike2 = 0.0; double DynamicPutStrike3 = 0.0; // Key Levels from Dashboard double LevelUpper1 = 0.0; double LevelUpper2 = 0.0; double LevelMid = 0.0; double LevelLower1 = 0.0; double LevelLower2 = 0.0; // Trading Statistics int DailyTradeCount = 0; double DailyPnL = 0.0; double DailyProfit = 0.0; double DailyLoss = 0.0; int ConsecutiveLosses = 0; datetime LastTradeTime = 0; datetime LastResetDate = 0; // Risk Management bool TradingEnabled = true; double EquityHigh = 0.0; double EquityLow = 0.0; // Indicator Handles int ATRHandle = INVALID_HANDLE; int MAFastHandle = INVALID_HANDLE; int MASlowHandle = INVALID_HANDLE; int RSIMainHandle = INVALID_HANDLE; // Dashboard long chart_id = 0; // Chart window ID int DashboardSubWindow = -1; color PanelColor = C'30,30,30'; color TextColor = clrWhite; color ProfitColor = clrLime; color LossColor = clrRed; color WarningColor = clrOrange; // News Filter bool NewsBlockActive = false; datetime NewsBlockStart = 0; datetime NewsBlockEnd = 0; // CSV Cache Variables double CachedFuturePrice = -1; string LoadedCSVPath = ""; bool CSVLoadLogged = false; datetime LastCSVReloadTime = 0; //=== NEW: Control Panel Variables === // Control Panel Position - Moved below dashboard int ControlPanelX = 10; int ControlPanelY = 210; // Input Values for Control Panel double PendingPriceValue = 0.0; double ManualLotSize = 0.5; int ManualSLPoints = 0; int ManualTPPoints = 300; // OI Data Input Values double ManualFuturePriceValue = 0.0; double CallStrike1Value = 0.0; double CallStrike2Value = 0.0; double CallStrike3Value = 0.0; double PutStrike1Value = 0.0; double PutStrike2Value = 0.0; double PutStrike3Value = 0.0; // Control Panel Object Names string ControlPanelObjects[] = { // Top Panel Objects "CP_TopPanel", "CP_CloseAll", // OI Data Panel Objects "CP_OIDataPanel", "CP_FuturePrice", "CP_CallStrike1", "CP_CallStrike2", "CP_CallStrike3", "CP_PutStrike1", "CP_PutStrike2", "CP_PutStrike3", "CP_UpdateOI", // Trading Panel Objects "CP_TradingPanel", "CP_PendingPrice", "CP_LotSize", "CP_SLPoints", "CP_TPPoints", "CP_Sell", "CP_Buy", "CP_PendingSell", "CP_PendingBuy", "CP_CloseAllPending", "CP_CloseSell", "CP_CloseBuy" }; //--- Global Variables for Line Management string DrawnLines[]; // Array to store drawn line names int MaxLines = 10; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Initialize trading objects Trade.SetExpertMagicNumber(InpMagicNumber); Trade.SetDeviationInPoints(InpMaxSlippage); Trade.SetTypeFilling(ORDER_FILLING_IOC); //--- Initialize symbol info SymbolInfo.Name(_Symbol); SymbolInfo.RefreshRates(); //--- Initialize Dynamic OI Variables with input parameters DynamicFuturePrice = InpManualFuturePrice; DynamicCallStrike1 = InpCallStrike1; DynamicCallStrike2 = InpCallStrike2; DynamicCallStrike3 = InpCallStrike3; DynamicPutStrike1 = InpPutStrike1; DynamicPutStrike2 = InpPutStrike2; DynamicPutStrike3 = InpPutStrike3; //--- Initialize OI Levels InitializeOILevels(); //--- Initialize Key Levels InitializeKeyLevels(); //--- Initialize indicators if(!InitializeIndicators()) { Print("Error initializing indicators"); return INIT_FAILED; } //--- Initialize risk management EquityHigh = AccountInfo.Equity(); EquityLow = AccountInfo.Equity(); //--- Initialize dashboard if(InpEnableDashboard) { chart_id = ChartID(); // Get current chart ID CreateDashboard(); CreateControlPanel(); // Create new control panel } //--- Auto-recovery: Check for existing positions if(InpUseAutoRecovery) { CheckExistingPositions(); } //--- Set timer for updates (every 1 second) EventSetTimer(1); //--- Initialize lines array ArrayResize(DrawnLines, MaxLines); for(int i = 0; i < MaxLines; i++) { DrawnLines[i] = ""; } Print("EA Initialized Successfully"); Print("Symbol: ", _Symbol); Print("Account Balance: ", AccountInfo.Balance()); Print("Trading Enabled: ", TradingEnabled); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Clean up dashboard and control panel if(InpEnableDashboard) { ObjectsDeleteAll(chart_id, 0, -1); } //--- Release indicator handles if(ATRHandle != INVALID_HANDLE) IndicatorRelease(ATRHandle); if(MAFastHandle != INVALID_HANDLE) IndicatorRelease(MAFastHandle); if(MASlowHandle != INVALID_HANDLE) IndicatorRelease(MASlowHandle); if(RSIMainHandle != INVALID_HANDLE) IndicatorRelease(RSIMainHandle); //--- Kill timer EventKillTimer(); Print("EA Deinitialized"); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Skip if not enough bars if(Bars(_Symbol, _Period) < 100) return; //--- Update market data if(!UpdateMarketData()) return; //--- Check global conditions if(!CheckGlobalConditions()) return; //--- Check for trade signals CheckTradingSignals(); //--- Manage existing positions ManagePositions(); //--- Update dashboard and control panel if(InpEnableDashboard) { UpdateDashboard(); UpdateControlPanel(); } } //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { //--- Update dashboard periodically if(InpEnableDashboard) { UpdateDashboard(); UpdateControlPanel(); } //--- Check news events if(InpAvoidHighImpactNews) { CheckNewsEvents(); } //--- Reset daily statistics if new day CheckDailyReset(); //--- CSV reload check if(InpOISource == OI_SOURCE_CSV_FILE && InpCSVReloadInterval > 0) { if(TimeCurrent() - LastCSVReloadTime >= InpCSVReloadInterval * 60) { Print("CSV Reload: Scheduled reload triggered"); LastCSVReloadTime = TimeCurrent(); CachedFuturePrice = -1; LoadOIFromCSV(); } } } //+------------------------------------------------------------------+ //| Chart Event Handler | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- Handle control panel button clicks if(id == CHARTEVENT_OBJECT_CLICK) { HandleControlPanelClick(sparam); } //--- Handle edit field changes (สำหรับรับค่าจาก Input Fields) if(id == CHARTEVENT_OBJECT_ENDEDIT) { // อัพเดทค่าทันทีเมื่อมีการแก้ไขข้อมูลใน Edit Fields UpdateInputValues(); Print("Edit field updated: ", sparam, " Value: ", ObjectGetString(chart_id, sparam, OBJPROP_TEXT)); } } //+------------------------------------------------------------------+ //| Create Control Panel | //+------------------------------------------------------------------+ void CreateControlPanel() { //--- Create top control panel CreateControlPanelUI(); //--- Set initial values UpdateControlPanelValues(); } //+------------------------------------------------------------------+ //| Create Control Panel UI | //+------------------------------------------------------------------+ void CreateControlPanelUI() { int panelWidth = 850; // Match dashboard width int topPanelHeight = 50; int oiPanelHeight = 120; int tradingPanelHeight = 180; //--- Top Panel (Quick Tools) CreatePanel("CP_TopPanel", ControlPanelX, ControlPanelY, panelWidth, topPanelHeight, C'45,45,45', BORDER_FLAT); //--- Close All Button CreateButton("CP_CloseAll", ControlPanelX + 10, ControlPanelY + 10, 830, 30, "Close All Positions", clrRed, clrWhite); //--- OI Data Panel (New Section) CreatePanel("CP_OIDataPanel", ControlPanelX, ControlPanelY + topPanelHeight + 10, panelWidth, oiPanelHeight, C'45,45,45', BORDER_FLAT); //--- OI Data Labels Row 1 - ปรับจัดเรียงให้เป็น Grid Layout CreateLabel("CP_FuturePriceLabel", ControlPanelX + 10, ControlPanelY + topPanelHeight + 20, "Future Price:", clrYellow, 8); CreateLabel("CP_CallStrikesLabel", ControlPanelX + 200, ControlPanelY + topPanelHeight + 20, "Call Strikes:", clrYellow, 8); CreateLabel("CP_PutStrikesLabel", ControlPanelX + 200, ControlPanelY + topPanelHeight + 50, "Put Strikes:", clrYellow, 8); //--- OI Data Fields Row 1 - จัดเรียงแบบ Grid CreateEditField("CP_FuturePrice", ControlPanelX + 90, ControlPanelY + topPanelHeight + 15, 80, 20, "0", clrWhite, clrBlack); CreateEditField("CP_CallStrike1", ControlPanelX + 280, ControlPanelY + topPanelHeight + 15, 80, 20, "0", clrWhite, clrBlack); CreateEditField("CP_CallStrike2", ControlPanelX + 370, ControlPanelY + topPanelHeight + 15, 80, 20, "0", clrWhite, clrBlack); CreateEditField("CP_CallStrike3", ControlPanelX + 460, ControlPanelY + topPanelHeight + 15, 80, 20, "0", clrWhite, clrBlack); //--- OI Data Fields Row 2 - จัดเรียงให้เสมอกันกับ Call Strikes CreateEditField("CP_PutStrike1", ControlPanelX + 280, ControlPanelY + topPanelHeight + 45, 80, 20, "0", clrWhite, clrBlack); CreateEditField("CP_PutStrike2", ControlPanelX + 370, ControlPanelY + topPanelHeight + 45, 80, 20, "0", clrWhite, clrBlack); CreateEditField("CP_PutStrike3", ControlPanelX + 460, ControlPanelY + topPanelHeight + 45, 80, 20, "0", clrWhite, clrBlack); //--- Update OI Button CreateButton("CP_UpdateOI", ControlPanelX + 750, ControlPanelY + topPanelHeight + 25, 90, 35, "Update OI Data", clrCyan, clrBlack); //--- Trading Panel (Main Trading Controls) CreatePanel("CP_TradingPanel", ControlPanelX, ControlPanelY + topPanelHeight + oiPanelHeight + 20, panelWidth, tradingPanelHeight, C'45,45,45', BORDER_FLAT); //--- Trading Input Fields Labels CreateLabel("CP_PendingPriceLabel", ControlPanelX + 10, ControlPanelY + topPanelHeight + oiPanelHeight + 35, "Price:", clrYellow, 8); CreateLabel("CP_LotSizeLabel", ControlPanelX + 10, ControlPanelY + topPanelHeight + oiPanelHeight + 60, "Lot Size:", clrYellow, 8); CreateLabel("CP_SLPointsLabel", ControlPanelX + 10, ControlPanelY + topPanelHeight + oiPanelHeight + 85, "SL (Point):", clrYellow, 8); CreateLabel("CP_TPPointsLabel", ControlPanelX + 10, ControlPanelY + topPanelHeight + oiPanelHeight + 110, "TP (Point):", clrYellow, 8); //--- Trading Input Fields CreateEditField("CP_PendingPrice", ControlPanelX + 80, ControlPanelY + topPanelHeight + oiPanelHeight + 30, 80, 20, "0", clrWhite, clrBlack); CreateEditField("CP_LotSize", ControlPanelX + 80, ControlPanelY + topPanelHeight + oiPanelHeight + 55, 80, 20, "0.5", clrWhite, clrBlack); CreateEditField("CP_SLPoints", ControlPanelX + 80, ControlPanelY + topPanelHeight + oiPanelHeight + 80, 80, 20, "0", clrWhite, clrBlack); CreateEditField("CP_TPPoints", ControlPanelX + 80, ControlPanelY + topPanelHeight + oiPanelHeight + 105, 80, 20, "300", clrWhite, clrBlack); //--- Immediate Action Buttons Row 1 CreateButton("CP_Sell", ControlPanelX + 180, ControlPanelY + topPanelHeight + oiPanelHeight + 30, 100, 25, "Sell Now", clrRed, clrWhite); CreateButton("CP_Buy", ControlPanelX + 290, ControlPanelY + topPanelHeight + oiPanelHeight + 30, 100, 25, "Buy Now", clrGreen, clrWhite); //--- Pending Action Buttons Row 2 CreateButton("CP_PendingSell", ControlPanelX + 180, ControlPanelY + topPanelHeight + oiPanelHeight + 65, 100, 25, "Pending Sell", clrOrange, clrBlack); CreateButton("CP_PendingBuy", ControlPanelX + 290, ControlPanelY + topPanelHeight + oiPanelHeight + 65, 100, 25, "Pending Buy", clrDarkGreen, clrWhite); //--- Management Buttons Row 3 CreateButton("CP_CloseAllPending", ControlPanelX + 180, ControlPanelY + topPanelHeight + oiPanelHeight + 100, 210, 25, "Close All Pending Orders", clrPurple, clrWhite); //--- Management Buttons Row 4 CreateButton("CP_CloseSell", ControlPanelX + 180, ControlPanelY + topPanelHeight + oiPanelHeight + 135, 100, 25, "Close All Sell", clrPurple, clrWhite); CreateButton("CP_CloseBuy", ControlPanelX + 290, ControlPanelY + topPanelHeight + oiPanelHeight + 135, 100, 25, "Close All Buy", clrPurple, clrWhite); } //+------------------------------------------------------------------+ //| Create Panel | //+------------------------------------------------------------------+ void CreatePanel(string name, int x, int y, int width, int height, color bgColor, ENUM_BORDER_TYPE border) { if(ObjectFind(chart_id, name) < 0) { ObjectCreate(chart_id, name, OBJ_RECTANGLE_LABEL, 0, 0, 0); ObjectSetInteger(chart_id, name, OBJPROP_XDISTANCE, x); ObjectSetInteger(chart_id, name, OBJPROP_YDISTANCE, y); ObjectSetInteger(chart_id, name, OBJPROP_XSIZE, width); ObjectSetInteger(chart_id, name, OBJPROP_YSIZE, height); ObjectSetInteger(chart_id, name, OBJPROP_BGCOLOR, bgColor); ObjectSetInteger(chart_id, name, OBJPROP_BORDER_TYPE, border); ObjectSetInteger(chart_id, name, OBJPROP_BORDER_COLOR, clrGray); ObjectSetInteger(chart_id, name, OBJPROP_BACK, false); // ปรับจาก true เป็น false เพื่อให้อยู่หน้ากราฟ ObjectSetInteger(chart_id, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(chart_id, name, OBJPROP_HIDDEN, true); // ตั้งค่า Z-Order ให้สูงเพื่อให้อยู่เหนือเส้นกราฟ ObjectSetInteger(chart_id, name, OBJPROP_ZORDER, 1000); } } //+------------------------------------------------------------------+ //| Create Button | //+------------------------------------------------------------------+ void CreateButton(string name, int x, int y, int width, int height, string text, color bgColor, color textColor) { if(ObjectFind(chart_id, name) < 0) { ObjectCreate(chart_id, name, OBJ_BUTTON, 0, 0, 0); ObjectSetInteger(chart_id, name, OBJPROP_XDISTANCE, x); ObjectSetInteger(chart_id, name, OBJPROP_YDISTANCE, y); ObjectSetInteger(chart_id, name, OBJPROP_XSIZE, width); ObjectSetInteger(chart_id, name, OBJPROP_YSIZE, height); ObjectSetString(chart_id, name, OBJPROP_TEXT, text); ObjectSetInteger(chart_id, name, OBJPROP_BGCOLOR, bgColor); ObjectSetInteger(chart_id, name, OBJPROP_COLOR, textColor); ObjectSetInteger(chart_id, name, OBJPROP_FONTSIZE, 8); ObjectSetString(chart_id, name, OBJPROP_FONT, "Arial"); ObjectSetInteger(chart_id, name, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(chart_id, name, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER); ObjectSetInteger(chart_id, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(chart_id, name, OBJPROP_HIDDEN, true); ObjectSetInteger(chart_id, name, OBJPROP_ZORDER, 1001); } } //+------------------------------------------------------------------+ //| Create Edit Field | //+------------------------------------------------------------------+ void CreateEditField(string name, int x, int y, int width, int height, string defaultText, color textColor, color bgColor) { if(ObjectFind(chart_id, name) < 0) { ObjectCreate(chart_id, name, OBJ_EDIT, 0, 0, 0); ObjectSetInteger(chart_id, name, OBJPROP_XDISTANCE, x); ObjectSetInteger(chart_id, name, OBJPROP_YDISTANCE, y); ObjectSetInteger(chart_id, name, OBJPROP_XSIZE, width); ObjectSetInteger(chart_id, name, OBJPROP_YSIZE, height); ObjectSetString(chart_id, name, OBJPROP_TEXT, defaultText); ObjectSetInteger(chart_id, name, OBJPROP_BGCOLOR, bgColor); ObjectSetInteger(chart_id, name, OBJPROP_COLOR, textColor); ObjectSetInteger(chart_id, name, OBJPROP_FONTSIZE, 8); ObjectSetString(chart_id, name, OBJPROP_FONT, "Arial"); ObjectSetInteger(chart_id, name, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(chart_id, name, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER); ObjectSetInteger(chart_id, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(chart_id, name, OBJPROP_HIDDEN, true); ObjectSetInteger(chart_id, name, OBJPROP_ZORDER, 1001); } } //+------------------------------------------------------------------+ //| Create Label | //+------------------------------------------------------------------+ void CreateLabel(string name, int x, int y, string text, color clr, int fontSize = 8) { if(ObjectFind(chart_id, name) < 0) { ObjectCreate(chart_id, name, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(chart_id, name, OBJPROP_XDISTANCE, x); ObjectSetInteger(chart_id, name, OBJPROP_YDISTANCE, y); ObjectSetString(chart_id, name, OBJPROP_TEXT, text); ObjectSetInteger(chart_id, name, OBJPROP_COLOR, clr); ObjectSetInteger(chart_id, name, OBJPROP_FONTSIZE, fontSize); ObjectSetString(chart_id, name, OBJPROP_FONT, "Arial"); ObjectSetInteger(chart_id, name, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(chart_id, name, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER); ObjectSetInteger(chart_id, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(chart_id, name, OBJPROP_HIDDEN, true); ObjectSetInteger(chart_id, name, OBJPROP_ZORDER, 1002); } } //+------------------------------------------------------------------+ //| Handle Control Panel Click | //+------------------------------------------------------------------+ void HandleControlPanelClick(string objectName) { //--- Get current values from input fields UpdateInputValues(); if(objectName == "CP_CloseAll") { CloseAllPositions(); } else if(objectName == "CP_UpdateOI") // New OI Data Update Handler { UpdateOIData(); } else if(objectName == "CP_Sell") { ExecuteManualSell(); } else if(objectName == "CP_Buy") { ExecuteManualBuy(); } else if(objectName == "CP_PendingSell") { ExecutePendingSell(); } else if(objectName == "CP_PendingBuy") { ExecutePendingBuy(); } else if(objectName == "CP_CloseAllPending") { CancelAllPendingOrders(); } else if(objectName == "CP_CloseSell") { CloseAllSellPositions(); } else if(objectName == "CP_CloseBuy") { CloseAllBuyPositions(); } } //+------------------------------------------------------------------+ //| Update Input Values from Control Panel | //+------------------------------------------------------------------+ void UpdateInputValues() { // Update trading input values PendingPriceValue = StringToDouble(ObjectGetString(chart_id, "CP_PendingPrice", OBJPROP_TEXT)); ManualLotSize = StringToDouble(ObjectGetString(chart_id, "CP_LotSize", OBJPROP_TEXT)); ManualSLPoints = (int)StringToInteger(ObjectGetString(chart_id, "CP_SLPoints", OBJPROP_TEXT)); ManualTPPoints = (int)StringToInteger(ObjectGetString(chart_id, "CP_TPPoints", OBJPROP_TEXT)); // Update OI data input values ManualFuturePriceValue = StringToDouble(ObjectGetString(chart_id, "CP_FuturePrice", OBJPROP_TEXT)); CallStrike1Value = StringToDouble(ObjectGetString(chart_id, "CP_CallStrike1", OBJPROP_TEXT)); CallStrike2Value = StringToDouble(ObjectGetString(chart_id, "CP_CallStrike2", OBJPROP_TEXT)); CallStrike3Value = StringToDouble(ObjectGetString(chart_id, "CP_CallStrike3", OBJPROP_TEXT)); PutStrike1Value = StringToDouble(ObjectGetString(chart_id, "CP_PutStrike1", OBJPROP_TEXT)); PutStrike2Value = StringToDouble(ObjectGetString(chart_id, "CP_PutStrike2", OBJPROP_TEXT)); PutStrike3Value = StringToDouble(ObjectGetString(chart_id, "CP_PutStrike3", OBJPROP_TEXT)); Print("Pending Price: ", PendingPriceValue); Print("Lot Size: ", ManualLotSize); Print("SL Points: ", ManualSLPoints); Print("TP Points: ", ManualTPPoints); Print("Manual Future Price: ", ManualFuturePriceValue); Print("Call Strikes: ", CallStrike1Value, ", ", CallStrike2Value, ", ", CallStrike3Value); Print("Put Strikes: ", PutStrike1Value, ", ", PutStrike2Value, ", ", PutStrike3Value); } //+------------------------------------------------------------------+ //| Update Control Panel Values | //+------------------------------------------------------------------+ void UpdateControlPanelValues() { // Update price display double currentBid = SymbolInfo.Bid(); ObjectSetString(chart_id, "CP_PendingPrice", OBJPROP_TEXT, DoubleToString(currentBid, _Digits)); // Initialize OI data fields with current input values (dynamic first, then input) ObjectSetString(chart_id, "CP_FuturePrice", OBJPROP_TEXT, DoubleToString((DynamicFuturePrice > 0) ? DynamicFuturePrice : InpManualFuturePrice, _Digits)); ObjectSetString(chart_id, "CP_CallStrike1", OBJPROP_TEXT, DoubleToString((DynamicCallStrike1 > 0) ? DynamicCallStrike1 : InpCallStrike1, _Digits)); ObjectSetString(chart_id, "CP_CallStrike2", OBJPROP_TEXT, DoubleToString((DynamicCallStrike2 > 0) ? DynamicCallStrike2 : InpCallStrike2, _Digits)); ObjectSetString(chart_id, "CP_CallStrike3", OBJPROP_TEXT, DoubleToString((DynamicCallStrike3 > 0) ? DynamicCallStrike3 : InpCallStrike3, _Digits)); ObjectSetString(chart_id, "CP_PutStrike1", OBJPROP_TEXT, DoubleToString((DynamicPutStrike1 > 0) ? DynamicPutStrike1 : InpPutStrike1, _Digits)); ObjectSetString(chart_id, "CP_PutStrike2", OBJPROP_TEXT, DoubleToString((DynamicPutStrike2 > 0) ? DynamicPutStrike2 : InpPutStrike2, _Digits)); ObjectSetString(chart_id, "CP_PutStrike3", OBJPROP_TEXT, DoubleToString((DynamicPutStrike3 > 0) ? DynamicPutStrike3 : InpPutStrike3, _Digits)); } //+------------------------------------------------------------------+ //| Update Control Panel | //+------------------------------------------------------------------+ void UpdateControlPanel() { // Update pending price with current market price periodically static datetime lastUpdate = 0; if(TimeCurrent() - lastUpdate > 5) // Update every 5 seconds { UpdateControlPanelValues(); lastUpdate = TimeCurrent(); } } //+------------------------------------------------------------------+ //| Execute Manual SELL | //+------------------------------------------------------------------+ void ExecuteManualSell() { double lotSize = NormalizeLot(ManualLotSize); if(lotSize <= 0) return; double sl = (ManualSLPoints > 0) ? SymbolInfo.Bid() + ManualSLPoints * _Point : 0.0; double tp = (ManualTPPoints > 0) ? SymbolInfo.Bid() - ManualTPPoints * _Point : 0.0; if(Trade.Sell(lotSize, _Symbol, SymbolInfo.Bid(), sl, tp, "Manual SELL")) { Print("Manual SELL executed. Lot: ", lotSize, " Price: ", SymbolInfo.Bid()); DailyTradeCount++; } else { Print("Manual SELL failed. Error: ", Trade.ResultRetcodeDescription()); } } //+------------------------------------------------------------------+ //| Execute Manual BUY | //+------------------------------------------------------------------+ void ExecuteManualBuy() { double lotSize = NormalizeLot(ManualLotSize); if(lotSize <= 0) return; double sl = (ManualSLPoints > 0) ? SymbolInfo.Ask() - ManualSLPoints * _Point : 0.0; double tp = (ManualTPPoints > 0) ? SymbolInfo.Ask() + ManualTPPoints * _Point : 0.0; if(Trade.Buy(lotSize, _Symbol, SymbolInfo.Ask(), sl, tp, "Manual BUY")) { Print("Manual BUY executed. Lot: ", lotSize, " Price: ", SymbolInfo.Ask()); DailyTradeCount++; } else { Print("Manual BUY failed. Error: ", Trade.ResultRetcodeDescription()); } } //+------------------------------------------------------------------+ //| Execute Pending SELL | //+------------------------------------------------------------------+ void ExecutePendingSell() { // 1. อัพเดทค่าจาก Control Panel UpdateInputValues(); // 2. Refresh rates ก่อนใช้งาน SymbolInfo.RefreshRates(); // 3. ตรวจสอบค่าที่ได้ if(PendingPriceValue <= 0) { Print("Invalid pending price for SELL: ", PendingPriceValue); Alert("กรุณากรอกราคาที่ต้องการสำหรับ Pending SELL"); return; } double lotSize = NormalizeLot(ManualLotSize); if(lotSize <= 0) { Print("Invalid lot size for Pending SELL: ", ManualLotSize); Alert("กรุณากรอกขนาด Lot ที่ถูกต้อง"); return; } // 4. คำนวณ SL และ TP สำหรับ SELL orders // SL ต้องต่ำกว่าราคา entry สำหรับ SELL orders double sl = (ManualSLPoints > 0) ? PendingPriceValue - ManualSLPoints * _Point : 0.0; double tp = (ManualTPPoints > 0) ? PendingPriceValue - ManualTPPoints * _Point : 0.0; Print("SELL Order - Entry: ", PendingPriceValue, " | SL: ", sl, " | TP: ", tp); // 5. กำหนดประเภท Order - SELL logic ต้องถูกต้อง ENUM_ORDER_TYPE orderType; double currentBid = SymbolInfo.Bid(); // ใช้ Bid สำหรับ SELL orders Print("Current Bid: ", currentBid, " | Pending Price: ", PendingPriceValue); // SELL_LIMIT: ขายที่ราคาสูงกว่าตลาดปัจจุบัน (รอราคาขึ้นมาแล้วขาย) // SELL_STOP: ขายเมื่อราคาลดลงถึงระดับที่กำหนด (Stop loss) if(PendingPriceValue > currentBid) { orderType = ORDER_TYPE_SELL_LIMIT; // Sell limit if price is above current Bid Print("Using SELL_LIMIT order type (sell at higher price)"); } else { orderType = ORDER_TYPE_SELL_STOP; // Sell stop if price is below current Bid Print("Using SELL_STOP order type (sell when price drops)"); } // 6. ใช้ OrderSend() ตรงๆ แทน Trade.OrderSend() MqlTradeRequest request = {}; MqlTradeResult result = {}; request.action = TRADE_ACTION_PENDING; request.symbol = _Symbol; request.type = orderType; request.volume = lotSize; request.price = NormalizeDouble(PendingPriceValue, _Digits); request.sl = (sl > 0) ? NormalizeDouble(sl, _Digits) : 0; request.tp = (tp > 0) ? NormalizeDouble(tp, _Digits) : 0; request.comment = "Pending SELL"; request.type_filling = ORDER_FILLING_IOC; request.magic = InpMagicNumber; // 7. ใช้ Trade.OrderSend() สำหรับการจัดการที่ดีกว่า if(Trade.OrderSend(request, result)) { Print("Pending SELL order placed successfully!"); Print("Ticket: ", result.order, " | Price: ", result.price); Alert("Pending SELL order placed successfully!\nTicket: " + IntegerToString(result.order) + "\nPrice: " + DoubleToString(result.price, _Digits)); } else { Print("Pending SELL failed! Error: ", Trade.ResultRetcodeDescription()); Alert("Pending SELL failed! Error: " + Trade.ResultRetcodeDescription()); } } //+------------------------------------------------------------------+ //| Execute Pending BUY | //+------------------------------------------------------------------+ void ExecutePendingBuy() { // 1. อัพเดทค่าจาก Control Panel UpdateInputValues(); // 2. Refresh rates ก่อนใช้งาน SymbolInfo.RefreshRates(); // 3. ตรวจสอบค่าที่ได้ if(PendingPriceValue <= 0) { Print("Invalid pending price for BUY: ", PendingPriceValue); Alert("กรุณากรอกราคาที่ต้องการสำหรับ Pending BUY"); return; } double lotSize = NormalizeLot(ManualLotSize); if(lotSize <= 0) { Print("Invalid lot size for Pending BUY: ", ManualLotSize); Alert("กรุณากรอกขนาด Lot ที่ถูกต้อง"); return; } // 4. คำนวณ SL และ TP สำหรับ BUY orders // SL ต้องต่ำกว่าราคา entry สำหรับ BUY orders double sl = (ManualSLPoints > 0) ? PendingPriceValue - ManualSLPoints * _Point : 0.0; double tp = (ManualTPPoints > 0) ? PendingPriceValue + ManualTPPoints * _Point : 0.0; Print("BUY Order - Entry: ", PendingPriceValue, " | SL: ", sl, " | TP: ", tp); // 5. กำหนดประเภท Order - BUY logic ENUM_ORDER_TYPE orderType; double currentAsk = SymbolInfo.Ask(); // ใช้ Ask สำหรับ BUY orders Print("Current Ask: ", currentAsk, " | Pending Price: ", PendingPriceValue); // BUY_LIMIT: ซื้อที่ราคาต่ำกว่าตลาดปัจจุบัน (รอราคาลงมาแล้วซื้อ) // BUY_STOP: ซื้อเมื่อราคาขึ้นไปถึงระดับที่กำหนด (Breakout) if(PendingPriceValue < currentAsk) { orderType = ORDER_TYPE_BUY_LIMIT; // Buy limit if price is below current Ask Print("Using BUY_LIMIT order type (buy at lower price)"); } else { orderType = ORDER_TYPE_BUY_STOP; // Buy stop if price is above current Ask Print("Using BUY_STOP order type (buy when price breaks higher)"); } // 6. เตรียม Trade Request MqlTradeRequest request = {}; MqlTradeResult result = {}; request.action = TRADE_ACTION_PENDING; request.symbol = _Symbol; request.type = orderType; request.volume = lotSize; request.price = NormalizeDouble(PendingPriceValue, _Digits); request.sl = (sl > 0) ? NormalizeDouble(sl, _Digits) : 0; request.tp = (tp > 0) ? NormalizeDouble(tp, _Digits) : 0; request.comment = "Pending BUY"; request.type_filling = ORDER_FILLING_IOC; request.magic = InpMagicNumber; // 7. ส่ง Order if(Trade.OrderSend(request, result)) { Print("Pending BUY order placed successfully!"); Print("Ticket: ", result.order, " | Price: ", result.price); Alert("Pending BUY order placed successfully!\nTicket: " + IntegerToString(result.order) + "\nPrice: " + DoubleToString(result.price, _Digits)); } else { Print("Pending BUY failed! Error: ", Trade.ResultRetcodeDescription()); Alert("Pending BUY failed! Error: " + Trade.ResultRetcodeDescription()); } } //+------------------------------------------------------------------+ //| Cancel All Pending Orders | //+------------------------------------------------------------------+ void CancelAllPendingOrders() { int totalOrders = OrdersTotal(); int canceledCount = 0; for(int i = totalOrders - 1; i >= 0; i--) { if(OrderInfo.SelectByIndex(i)) { if(OrderInfo.Symbol() == _Symbol && OrderInfo.Magic() == InpMagicNumber) { ENUM_ORDER_TYPE orderType = OrderInfo.OrderType(); // Check if it's a pending order if(orderType == ORDER_TYPE_BUY_LIMIT || orderType == ORDER_TYPE_BUY_STOP || orderType == ORDER_TYPE_SELL_LIMIT || orderType == ORDER_TYPE_SELL_STOP) { if(Trade.OrderDelete(OrderInfo.Ticket())) { Print("Pending order deleted: ", OrderInfo.Ticket()); canceledCount++; } } } } } if(canceledCount > 0) { Print("Total pending orders canceled: ", canceledCount); } else { Print("No pending orders found to cancel"); } } //+------------------------------------------------------------------+ //| Close All Sell Positions | //+------------------------------------------------------------------+ void CloseAllSellPositions() { for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionInfo.SelectByIndex(i)) { if(PositionInfo.Symbol() == _Symbol && PositionInfo.Magic() == InpMagicNumber && PositionInfo.PositionType() == POSITION_TYPE_SELL) { Trade.PositionClose(PositionInfo.Ticket()); Print("SELL position closed: ", PositionInfo.Ticket()); } } } } //+------------------------------------------------------------------+ //| Close All Buy Positions | //+------------------------------------------------------------------+ void CloseAllBuyPositions() { for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionInfo.SelectByIndex(i)) { if(PositionInfo.Symbol() == _Symbol && PositionInfo.Magic() == InpMagicNumber && PositionInfo.PositionType() == POSITION_TYPE_BUY) { Trade.PositionClose(PositionInfo.Ticket()); Print("BUY position closed: ", PositionInfo.Ticket()); } } } } //+------------------------------------------------------------------+ //| Normalize Lot Size | //+------------------------------------------------------------------+ double NormalizeLot(double lot) { double minLot = SymbolInfo.LotsMin(); double maxLot = SymbolInfo.LotsMax(); double lotStep = SymbolInfo.LotsStep(); lot = MathMax(lot, minLot); lot = MathMin(lot, maxLot); if(lotStep > 0) lot = MathRound(lot / lotStep) * lotStep; return NormalizeDouble(lot, 2); } //+------------------------------------------------------------------+ //| Update OI Data from Control Panel | //+------------------------------------------------------------------+ void UpdateOIData() { // Get current values from control panel UpdateInputValues(); // แสดงข้อมูลที่ได้รับจาก Control Panel Print("=== OI Data Update Process ==="); Print("Manual Future Price Value: ", ManualFuturePriceValue); Print("Call Strike Values: ", CallStrike1Value, ", ", CallStrike2Value, ", ", CallStrike3Value); Print("Put Strike Values: ", PutStrike1Value, ", ", PutStrike2Value, ", ", PutStrike3Value); // Validate and update dynamic variables if(ManualFuturePriceValue > 0) { DynamicFuturePrice = ManualFuturePriceValue; Print("Updated Dynamic Future Price: ", DynamicFuturePrice); } else { Print("Warning: Manual Future Price is 0 or invalid, using previous value: ", DynamicFuturePrice); } // Update OI Strike levels (dynamic variables) if(CallStrike1Value >= 0) DynamicCallStrike1 = CallStrike1Value; if(CallStrike2Value >= 0) DynamicCallStrike2 = CallStrike2Value; if(CallStrike3Value >= 0) DynamicCallStrike3 = CallStrike3Value; if(PutStrike1Value >= 0) DynamicPutStrike1 = PutStrike1Value; if(PutStrike2Value >= 0) DynamicPutStrike2 = PutStrike2Value; if(PutStrike3Value >= 0) DynamicPutStrike3 = PutStrike3Value; Print("Dynamic Call Strikes: ", DynamicCallStrike1, ", ", DynamicCallStrike2, ", ", DynamicCallStrike3); Print("Dynamic Put Strikes: ", DynamicPutStrike1, ", ", DynamicPutStrike2, ", ", DynamicPutStrike3); // Reinitialize OI levels with new values InitializeOILevels(); InitializeKeyLevels(); Print("OI Data Updated Successfully!"); // Show confirmation string message = "OI Data Updated Successfully!\n" + "Future Price: " + DoubleToString(DynamicFuturePrice, _Digits) + "\n" + "Call Strikes: " + DoubleToString(DynamicCallStrike1, _Digits) + ", " + DoubleToString(DynamicCallStrike2, _Digits) + ", " + DoubleToString(DynamicCallStrike3, _Digits) + "\n" + "Put Strikes: " + DoubleToString(DynamicPutStrike1, _Digits) + ", " + DoubleToString(DynamicPutStrike2, _Digits) + ", " + DoubleToString(DynamicPutStrike3, _Digits); Alert(message); } //+------------------------------------------------------------------+ //| Initialize OI Levels | //+------------------------------------------------------------------+ void InitializeOILevels() { //--- Load OI data based on source switch(InpOISource) { case OI_SOURCE_MANUAL: // Use dynamic variables first, fall back to input parameters CallLevels[0] = (DynamicCallStrike1 > 0) ? DynamicCallStrike1 : InpCallStrike1; CallLevels[1] = (DynamicCallStrike2 > 0) ? DynamicCallStrike2 : InpCallStrike2; CallLevels[2] = (DynamicCallStrike3 > 0) ? DynamicCallStrike3 : InpCallStrike3; PutLevels[0] = (DynamicPutStrike1 > 0) ? DynamicPutStrike1 : InpPutStrike1; PutLevels[1] = (DynamicPutStrike2 > 0) ? DynamicPutStrike2 : InpPutStrike2; PutLevels[2] = (DynamicPutStrike3 > 0) ? DynamicPutStrike3 : InpPutStrike3; // Default OI values CallOI[0] = 1000; CallOI[1] = 800; CallOI[2] = 600; PutOI[0] = 1000; PutOI[1] = 800; PutOI[2] = 600; break; case OI_SOURCE_CSV_FILE: LoadOIFromCSV(); break; case OI_SOURCE_AUTO_SYNC: // Future implementation for auto-sync break; } //--- Sort levels in ascending order ArraySort(CallLevels); ArraySort(PutLevels); Print("OI Levels Initialized:"); Print("Call Levels: ", CallLevels[0], ", ", CallLevels[1], ", ", CallLevels[2]); Print("Put Levels: ", PutLevels[0], ", ", PutLevels[1], ", ", PutLevels[2]); } //+------------------------------------------------------------------+ //| Load OI data from CSV file | //+------------------------------------------------------------------+ void LoadOIFromCSV() { Print("=== CSV Loading Debug ==="); Print("Terminal Files dir: ", TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\Files\\"); Print("Attempting to open: ", InpOICsvPath); if(!FileIsExist(InpOICsvPath)) { Print("CSV FILE NOT FOUND - check path or if scraper output exists"); return; } string filename = InpOICsvPath; int filehandle = FileOpen(filename, FILE_READ | FILE_ANSI); if(filehandle == INVALID_HANDLE) { int err = GetLastError(); Print("CSV OPEN FAILED [Error ", err, "]: ", err); Print("File exists: ", FileIsExist(InpOICsvPath) ? "YES" : "NO"); InitializeOILevels(); return; } long fileSize = FileSize(filehandle); Print("CSV opened successfully. Size: ", fileSize, " bytes"); int callIndex = 0; int putIndex = 0; double futurePrice = 0.0; bool isFirstLine = true; while(!FileIsEnding(filehandle)) { string row = FileReadString(filehandle); Print("DEBUG ROW: [", row, "]"); if(isFirstLine) { isFirstLine = false; if(StringFind(row, "Type") >= 0 || StringFind(row, "Strike") >= 0 || StringFind(row, "OI") >= 0) { continue; } } string data[]; int count = StringSplit(row, ',', data); if(count >= 3) { string type = data[0]; double strike = StringToDouble(data[1]); int oi = (int)StringToInteger(data[2]); if(StringFind(type, "Future") >= 0) { futurePrice = strike; if(!CSVLoadLogged) { Print("DEBUG: Parsed Future price: ", futurePrice); } } else if(type == "CALL" && callIndex < 3) { CallLevels[callIndex] = strike; CallOI[callIndex] = oi; callIndex++; if(!CSVLoadLogged) { Print("DEBUG: Found CALL row, strike=", strike, " oi=", oi); } } else if(type == "PUT" && putIndex < 3) { PutLevels[putIndex] = strike; PutOI[putIndex] = oi; putIndex++; if(!CSVLoadLogged) { Print("DEBUG: Found PUT row, strike=", strike, " oi=", oi); } } } } FileClose(filehandle); for(int i = callIndex; i < 3; i++) { CallLevels[i] = 0; CallOI[i] = 0; } for(int i = putIndex; i < 3; i++) { PutLevels[i] = 0; PutOI[i] = 0; } if(futurePrice > 0) { CachedFuturePrice = futurePrice; DynamicFuturePrice = futurePrice; FuturePrice = futurePrice; LoadedCSVPath = filename; CSVLoadLogged = true; Print("CSV SUCCESS: FuturePrice=", futurePrice, ", CALL=[", CallLevels[0], ",", CallLevels[1], ",", CallLevels[2], "], PUT=[", PutLevels[0], ",", PutLevels[1], ",", PutLevels[2], "] loaded from ", filename); } else { if(!CSVLoadLogged) { Print("CSV ERROR: No valid price found in ", filename); CSVLoadLogged = true; } CachedFuturePrice = 0; } } //+------------------------------------------------------------------+ //| Initialize Key Levels | //+------------------------------------------------------------------+ void InitializeKeyLevels() { // Calculate key levels based on OI levels if(PutLevels[2] > 0 && CallLevels[2] > 0) { LevelLower2 = PutLevels[2]; // Lowest put strike LevelLower1 = PutLevels[1]; // Middle put strike LevelMid = (PutLevels[0] + CallLevels[0]) / 2; // Mid point LevelUpper1 = CallLevels[1]; // Middle call strike LevelUpper2 = CallLevels[2]; // Highest call strike } else { // Use current price as reference double currentPrice = SymbolInfo.Bid(); LevelLower2 = currentPrice - 500 * _Point; LevelLower1 = currentPrice - 250 * _Point; LevelMid = currentPrice; LevelUpper1 = currentPrice + 250 * _Point; LevelUpper2 = currentPrice + 500 * _Point; } } //+------------------------------------------------------------------+ //| Initialize Indicators | //+------------------------------------------------------------------+ bool InitializeIndicators() { // ATR for volatility ATRHandle = iATR(_Symbol, PERIOD_H1, 14); if(ATRHandle == INVALID_HANDLE) { Print("Error creating ATR indicator"); return false; } // Moving Averages for trend MAFastHandle = iMA(_Symbol, InpTrendTF, InpTrendMAPeriod1, 0, MODE_EMA, PRICE_CLOSE); MASlowHandle = iMA(_Symbol, InpTrendTF, InpTrendMAPeriod2, 0, MODE_SMA, PRICE_CLOSE); if(MAFastHandle == INVALID_HANDLE || MASlowHandle == INVALID_HANDLE) { Print("Error creating MA indicators"); return false; } // RSI for momentum (optional) RSIMainHandle = iRSI(_Symbol, PERIOD_H1, 14, PRICE_CLOSE); return true; } //+------------------------------------------------------------------+ //| Update Market Data | //+------------------------------------------------------------------+ bool UpdateMarketData() { // Get current prices SpotPrice = SymbolInfo.Bid(); SymbolInfo.RefreshRates(); // Get future price (CSV cache, manual or from symbol) if(CachedFuturePrice > 0) { FuturePrice = CachedFuturePrice; } else if(DynamicFuturePrice > 0) { FuturePrice = DynamicFuturePrice; } else if(InpManualFuturePrice > 0) { FuturePrice = InpManualFuturePrice; } else { // In real implementation, you might get this from a different symbol // For now, use spot price with a small offset FuturePrice = SpotPrice; } // Calculate Delta double previousDelta = DeltaPrice; DeltaPrice = FuturePrice - SpotPrice; // Update Delta EMA UpdateDeltaEMA(); // Calculate Deviation DeltaDeviation = DeltaPrice - DeltaEMA; // Check for zero cross if(InpCloseOnZeroDeviation && previousDelta * DeltaPrice < 0) { CloseAllPositions(); } return true; } //+------------------------------------------------------------------+ //| Update Delta EMA | //+------------------------------------------------------------------+ void UpdateDeltaEMA() { static double ema = 0; static bool firstRun = true; if(firstRun) { ema = DeltaPrice; firstRun = false; } else { double alpha = 2.0 / (InpDeltaEmaPeriod + 1.0); ema = alpha * DeltaPrice + (1 - alpha) * ema; } DeltaEMA = ema; } //+------------------------------------------------------------------+ //| Check Global Trading Conditions | //+------------------------------------------------------------------+ bool CheckGlobalConditions() { //--- Check if trading is enabled if(!TradingEnabled) { return false; } //--- Check terminal connection if(!TerminalInfoInteger(TERMINAL_CONNECTED)) { Print("Terminal not connected"); return false; } //--- Check trading permission if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) || !MQLInfoInteger(MQL_TRADE_ALLOWED)) { Print("Trading not allowed"); return false; } //--- Check spread limit long spread = SymbolInfo.Spread(); if(spread > InpMaxSpread) { return false; } //--- Check session time if(InpUseSessionFilter && !IsTradingSession()) { return false; } //--- Check news block if(NewsBlockActive) { return false; } //--- Check volatility filter if(InpUseATRFilter && !CheckVolatilityFilter()) { return false; } //--- Check daily limits if(!CheckDailyLimits()) { TradingEnabled = false; Print("Daily limit reached. Trading disabled."); return false; } //--- Check equity protection if(CheckEquityProtection()) { TradingEnabled = false; Print("Equity protection triggered. Trading disabled."); return false; } return true; } //+------------------------------------------------------------------+ //| Check Trading Session | //+------------------------------------------------------------------+ bool IsTradingSession() { MqlDateTime timeNow; TimeCurrent(timeNow); int hour = timeNow.hour; // Check if current hour is within trading session if(hour >= InpSessionStartHour && hour < InpSessionEndHour) { return true; } return false; } //+------------------------------------------------------------------+ //| Check Volatility Filter | //+------------------------------------------------------------------+ bool CheckVolatilityFilter() { double atrValues[]; ArraySetAsSeries(atrValues, true); if(CopyBuffer(ATRHandle, 0, 0, 1, atrValues) < 1) return false; double atr = atrValues[0]; double atrPercent = (atr / SpotPrice) * 100; return (atrPercent <= InpMaxATRPercent); } //+------------------------------------------------------------------+ //| Check Daily Limits | //+------------------------------------------------------------------+ bool CheckDailyLimits() { double balance = AccountInfo.Balance(); double equity = AccountInfo.Equity(); // Check daily loss limit if(DailyLoss > 0) { double lossPercent = (DailyLoss / balance) * 100; if(lossPercent >= InpMaxDailyLossPercent) { Print("Daily loss limit reached: ", lossPercent, "%"); return false; } } // Check daily profit limit if(DailyProfit > 0) { double profitPercent = (DailyProfit / balance) * 100; if(profitPercent >= InpMaxDailyProfitPercent) { Print("Daily profit limit reached: ", profitPercent, "%"); return false; } } // Check consecutive losses if(ConsecutiveLosses >= InpMaxConsecutiveLosses && InpDisableAfterMaxLoss) { Print("Max consecutive losses reached: ", ConsecutiveLosses); return false; } // Check daily trade count if(DailyTradeCount >= InpMaxDailyTrades) { Print("Max daily trades reached: ", DailyTradeCount); return false; } return true; } //+------------------------------------------------------------------+ //| Check Equity Protection | //+------------------------------------------------------------------+ bool CheckEquityProtection() { double balance = AccountInfo.Balance(); double equity = AccountInfo.Equity(); if(balance <= 0) return false; double drawdownPercent = ((balance - equity) / balance) * 100; if(drawdownPercent >= InpEquityProtectionPercent) { Print("Equity protection triggered: ", drawdownPercent, "% drawdown"); CloseAllPositions(); return true; } return false; } //+------------------------------------------------------------------+ //| Check News Events | //+------------------------------------------------------------------+ void CheckNewsEvents() { // Simplified news check - in real implementation, you would use a news API // or economic calendar data datetime timeNow = TimeCurrent(); // For now, this is a placeholder function // In production, you would implement actual news checking logic here } //+------------------------------------------------------------------+ //| Check Trading Signals | //+------------------------------------------------------------------+ void CheckTradingSignals() { // Don't open new trades if already have position if(PositionsTotal() > 0) { // Check if we should add to position or close return; } // Get market phase ENUM_MARKET_PHASE marketPhase = GetMarketPhase(); // Check SELL conditions if(CheckSellConditions(marketPhase)) { ExecuteSellTrade(); } // Check BUY conditions else if(CheckBuyConditions(marketPhase)) { ExecuteBuyTrade(); } } //+------------------------------------------------------------------+ //| Get Market Phase | //+------------------------------------------------------------------+ ENUM_MARKET_PHASE GetMarketPhase() { if(!InpUseMarketPhaseFilter) return PHASE_NEUTRAL; double maFast[], maSlow[]; ArraySetAsSeries(maFast, true); ArraySetAsSeries(maSlow, true); if(CopyBuffer(MAFastHandle, 0, 0, 2, maFast) < 2) return PHASE_NEUTRAL; if(CopyBuffer(MASlowHandle, 0, 0, 1, maSlow) < 1) return PHASE_NEUTRAL; double maFastCurrent = maFast[0]; double maFastPrevious = maFast[1]; double maSlowCurrent = maSlow[0]; // Calculate slope double slope = maFastCurrent - maFastPrevious; if(maFastCurrent > maSlowCurrent && slope > 0) return PHASE_BULLISH; else if(maFastCurrent < maSlowCurrent && slope < 0) return PHASE_BEARISH; else if(MathAbs(slope) < 0.0001) return PHASE_SIDEWAYS; else return PHASE_NEUTRAL; } //+------------------------------------------------------------------+ //| Check SELL Conditions | //+------------------------------------------------------------------+ bool CheckSellConditions(ENUM_MARKET_PHASE marketPhase) { // 1. Check Delta Deviation if(DeltaDeviation <= InpDeviationThreshold * _Point) return false; // 2. Check price near upper OI level if(!IsPriceNearLevel(SpotPrice, CallLevels, true)) return false; // 3. Check market phase filter if(marketPhase == PHASE_BULLISH && InpUseMarketPhaseFilter) { // Need stronger signal in bullish phase if(DeltaDeviation < InpDeviationThreshold * _Point * InpDeviationMultiplier) return false; } // 4. Check overbought condition (optional) if(!CheckOverbought()) return false; // 5. Check if price is at key resistance if(!IsAtResistance()) return false; return true; } //+------------------------------------------------------------------+ //| Check BUY Conditions | //+------------------------------------------------------------------+ bool CheckBuyConditions(ENUM_MARKET_PHASE marketPhase) { // 1. Check Delta Deviation if(DeltaDeviation >= -InpDeviationThreshold * _Point) return false; // 2. Check price near lower OI level if(!IsPriceNearLevel(SpotPrice, PutLevels, false)) return false; // 3. Check market phase filter if(marketPhase == PHASE_BEARISH && InpUseMarketPhaseFilter) { // Need stronger signal in bearish phase if(DeltaDeviation > -InpDeviationThreshold * _Point * InpDeviationMultiplier) return false; } // 4. Check oversold condition (optional) if(!CheckOversold()) return false; // 5. Check if price is at key support if(!IsAtSupport()) return false; return true; } //+------------------------------------------------------------------+ //| Check if price is near OI level | //+------------------------------------------------------------------+ bool IsPriceNearLevel(double price, double &levels[], bool isUpper) { double tolerance = 50 * _Point; // 50 points tolerance for(int i = 0; i < ArraySize(levels); i++) { if(levels[i] <= 0) continue; if(isUpper) { // For upper levels, price should be at or above the level if(price >= levels[i] - tolerance && price <= levels[i] + tolerance) return true; } else { // For lower levels, price should be at or below the level if(price >= levels[i] - tolerance && price <= levels[i] + tolerance) return true; } } return false; } //+------------------------------------------------------------------+ //| Check overbought condition | //+------------------------------------------------------------------+ bool CheckOverbought() { // Simple RSI check if(RSIMainHandle != INVALID_HANDLE) { double rsiValues[]; ArraySetAsSeries(rsiValues, true); if(CopyBuffer(RSIMainHandle, 0, 0, 1, rsiValues) >= 1) { if(rsiValues[0] < 70) // Not overbought return false; } } return true; } //+------------------------------------------------------------------+ //| Check oversold condition | //+------------------------------------------------------------------+ bool CheckOversold() { // Simple RSI check if(RSIMainHandle != INVALID_HANDLE) { double rsiValues[]; ArraySetAsSeries(rsiValues, true); if(CopyBuffer(RSIMainHandle, 0, 0, 1, rsiValues) >= 1) { if(rsiValues[0] > 30) // Not oversold return false; } } return true; } //+------------------------------------------------------------------+ //| Check if price is at resistance | //+------------------------------------------------------------------+ bool IsAtResistance() { // Check if price is near upper key levels double tolerance = 100 * _Point; if(MathAbs(SpotPrice - LevelUpper1) <= tolerance || MathAbs(SpotPrice - LevelUpper2) <= tolerance) { return true; } return false; } //+------------------------------------------------------------------+ //| Check if price is at support | //+------------------------------------------------------------------+ bool IsAtSupport() { // Check if price is near lower key levels double tolerance = 100 * _Point; if(MathAbs(SpotPrice - LevelLower1) <= tolerance || MathAbs(SpotPrice - LevelLower2) <= tolerance) { return true; } return false; } //+------------------------------------------------------------------+ //| Execute SELL Trade | //+------------------------------------------------------------------+ void ExecuteSellTrade() { double lotSize = CalculateLotSize(POSITION_TYPE_SELL); if(lotSize <= 0) return; double sl = CalculateStopLoss(POSITION_TYPE_SELL, SpotPrice); double tp = CalculateTakeProfit(POSITION_TYPE_SELL, SpotPrice); // Send sell order if(Trade.Sell(lotSize, _Symbol, SpotPrice, sl, tp, "OI Mean Reversion SELL")) { DailyTradeCount++; LastTradeTime = TimeCurrent(); Print("SELL order executed. Lot: ", lotSize, " Price: ", SpotPrice); } else { Print("SELL order failed. Error: ", Trade.ResultRetcodeDescription()); } } //+------------------------------------------------------------------+ //| Execute BUY Trade | //+------------------------------------------------------------------+ void ExecuteBuyTrade() { double lotSize = CalculateLotSize(POSITION_TYPE_BUY); if(lotSize <= 0) return; double sl = CalculateStopLoss(POSITION_TYPE_BUY, SpotPrice); double tp = CalculateTakeProfit(POSITION_TYPE_BUY, SpotPrice); // Send buy order if(Trade.Buy(lotSize, _Symbol, SpotPrice, sl, tp, "OI Mean Reversion BUY")) { DailyTradeCount++; LastTradeTime = TimeCurrent(); Print("BUY order executed. Lot: ", lotSize, " Price: ", SpotPrice); } else { Print("BUY order failed. Error: ", Trade.ResultRetcodeDescription()); } } //+------------------------------------------------------------------+ //| Calculate Lot Size | //+------------------------------------------------------------------+ double CalculateLotSize(ENUM_POSITION_TYPE tradeType) { if(!InpUseMoneyManagement) return NormalizeLot(InpLotSize); double balance = AccountInfo.Balance(); double riskAmount = balance * (InpRiskPercent / 100.0); double stopLossPrice = CalculateStopLoss(tradeType, SpotPrice); double stopLossPoints = MathAbs(SpotPrice - stopLossPrice) / _Point; double tickValue = SymbolInfo.TickValue(); double lotSize = riskAmount / (stopLossPoints * tickValue); return NormalizeLot(lotSize); } //+------------------------------------------------------------------+ //| Calculate Stop Loss | //+------------------------------------------------------------------+ double CalculateStopLoss(ENUM_POSITION_TYPE tradeType, double entryPrice) { if(!InpUseStopLoss) return 0.0; double slPoints = InpStopLossPoints * _Point; if(tradeType == POSITION_TYPE_BUY) return entryPrice - slPoints; else return entryPrice + slPoints; } //+------------------------------------------------------------------+ //| Calculate Take Profit | //+------------------------------------------------------------------+ double CalculateTakeProfit(ENUM_POSITION_TYPE tradeType, double entryPrice) { if(!InpUseTakeProfit) return 0.0; double tpPoints = InpTakeProfitPoints * _Point; if(tradeType == POSITION_TYPE_BUY) return entryPrice + tpPoints; else return entryPrice - tpPoints; } //+------------------------------------------------------------------+ //| Manage Positions | //+------------------------------------------------------------------+ void ManagePositions() { for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionInfo.SelectByIndex(i)) { if(PositionInfo.Symbol() == _Symbol && PositionInfo.Magic() == InpMagicNumber) { // Check for trailing stop if(InpUseTrailingStop) { ApplyTrailingStop(i); } // Check for soft stop loss if(InpUseSoftStopLoss) { CheckSoftStopLoss(i); } // Check for take profit at mid level CheckMidLevelExit(i); } } } } //+------------------------------------------------------------------+ //| Apply Trailing Stop | //+------------------------------------------------------------------+ void ApplyTrailingStop(int positionIndex) { if(!PositionInfo.SelectByIndex(positionIndex)) return; double currentPrice = PositionInfo.PositionType() == POSITION_TYPE_BUY ? SymbolInfo.Bid() : SymbolInfo.Ask(); double openPrice = PositionInfo.PriceOpen(); double currentSL = PositionInfo.StopLoss(); double pointsProfit = MathAbs(currentPrice - openPrice) / _Point; if(pointsProfit > InpTrailingStartPoints) { double newSL = 0; double trailPoints = InpTrailingStepPoints * _Point; if(PositionInfo.PositionType() == POSITION_TYPE_BUY) { newSL = currentPrice - trailPoints; if(newSL > currentSL && newSL > openPrice) { Trade.PositionModify(_Symbol, newSL, PositionInfo.TakeProfit()); } } else { newSL = currentPrice + trailPoints; if(newSL < currentSL && newSL < openPrice) { Trade.PositionModify(_Symbol, newSL, PositionInfo.TakeProfit()); } } } } //+------------------------------------------------------------------+ //| Check Soft Stop Loss | //+------------------------------------------------------------------+ void CheckSoftStopLoss(int positionIndex) { if(!PositionInfo.SelectByIndex(positionIndex)) return; double currentPrice = PositionInfo.PositionType() == POSITION_TYPE_BUY ? SymbolInfo.Bid() : SymbolInfo.Ask(); double stopLossPrice = CalculateStopLoss(PositionInfo.PositionType(), PositionInfo.PriceOpen()); if((PositionInfo.PositionType() == POSITION_TYPE_BUY && currentPrice <= stopLossPrice) || (PositionInfo.PositionType() == POSITION_TYPE_SELL && currentPrice >= stopLossPrice)) { Trade.PositionClose(_Symbol); ConsecutiveLosses++; DailyLoss += PositionInfo.Profit(); } } //+------------------------------------------------------------------+ //| Check Mid Level Exit | //+------------------------------------------------------------------+ void CheckMidLevelExit(int positionIndex) { if(!PositionInfo.SelectByIndex(positionIndex)) return; double currentPrice = PositionInfo.PositionType() == POSITION_TYPE_BUY ? SymbolInfo.Bid() : SymbolInfo.Ask(); // Close if price reaches mid level if(MathAbs(currentPrice - LevelMid) <= 10 * _Point) { Trade.PositionClosePartial(_Symbol, PositionInfo.Volume() * 0.5); // Close half } } //+------------------------------------------------------------------+ //| Close All Positions | //+------------------------------------------------------------------+ void CloseAllPositions() { for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionInfo.SelectByIndex(i)) { if(PositionInfo.Symbol() == _Symbol && PositionInfo.Magic() == InpMagicNumber) { Trade.PositionClose(_Symbol); } } } } //+------------------------------------------------------------------+ //| Check Existing Positions (Auto Recovery) | //+------------------------------------------------------------------+ void CheckExistingPositions() { int count = 0; for(int i = 0; i < PositionsTotal(); i++) { if(PositionInfo.SelectByIndex(i)) { if(PositionInfo.Symbol() == _Symbol && PositionInfo.Magic() == InpMagicNumber) { count++; Print("Found existing position #", i+1, " Type: ", EnumToString(PositionInfo.PositionType()), " Volume: ", PositionInfo.Volume(), " Profit: ", PositionInfo.Profit()); } } } if(count > 0) { Print("Auto-recovery: ", count, " positions restored"); } } //+------------------------------------------------------------------+ //| Check Daily Reset | //+------------------------------------------------------------------+ void CheckDailyReset() { MqlDateTime timeNow, lastReset; TimeCurrent(timeNow); TimeToStruct(LastResetDate, lastReset); if(timeNow.day != lastReset.day || timeNow.mon != lastReset.mon || timeNow.year != lastReset.year) { // New day, reset statistics DailyTradeCount = 0; DailyPnL = 0.0; DailyProfit = 0.0; DailyLoss = 0.0; ConsecutiveLosses = 0; LastResetDate = TimeCurrent(); TradingEnabled = true; Print("Daily statistics reset"); } } //+------------------------------------------------------------------+ //| Create Dashboard | //+------------------------------------------------------------------+ void CreateDashboard() { // Create main panel - ตั้งค่า Z-Order ให้สูงขึ้น CreatePanel("MainPanel", 10, 20, 850, 180, PanelColor, BORDER_SUNKEN); // Create section panels - ตั้งค่า Z-Order ให้สูงขึ้น และ BACK = false CreatePanel("PricePanel", 20, 30, 260, 70, C'40,40,40', BORDER_FLAT); CreatePanel("DeltaPanel", 290, 30, 260, 70, C'40,40,40', BORDER_FLAT); CreatePanel("LevelsPanel", 560, 30, 290, 70, C'40,40,40', BORDER_FLAT); CreatePanel("StatsPanel", 20, 110, 410, 80, C'40,40,40', BORDER_FLAT); CreatePanel("TradingPanel", 440, 110, 410, 80, C'40,40,40', BORDER_FLAT); // Initial labels UpdateDashboard(); } //+------------------------------------------------------------------+ //| Update Dashboard | //+------------------------------------------------------------------+ void UpdateDashboard() { // Calculate metrics double balance = AccountInfo.Balance(); double equity = AccountInfo.Equity(); double dailyGainPercent = balance > 0 ? ((equity - balance) / balance) * 100 : 0; double maxDD = balance > 0 ? ((balance - equity) / balance) * 100 : 0; // Get ATR double atrValues[]; ArraySetAsSeries(atrValues, true); double atr = 0; double atrPercent = 0; if(CopyBuffer(ATRHandle, 0, 0, 1, atrValues) >= 1) { atr = atrValues[0]; atrPercent = (atr / SpotPrice) * 100; } // Get market phase string marketPhase = GetMarketPhaseString(); color phaseColor = GetMarketPhaseColor(); // Calculate distances to key levels double distToUpper = LevelUpper2 > 0 ? ((LevelUpper2 - SpotPrice) / SpotPrice) * 100 : 0; double distToLower = LevelLower2 > 0 ? ((SpotPrice - LevelLower2) / SpotPrice) * 100 : 0; // Find nearest OI levels double nearestCall = FindNearestLevel(SpotPrice, CallLevels, true); double nearestPut = FindNearestLevel(SpotPrice, PutLevels, false); // Price Section UpdateLabel("PriceTitle", 30, 35, "PRICE INFORMATION", clrYellow, 10); UpdateLabel("SpotPrice", 30, 55, "Spot: " + DoubleToString(SpotPrice, _Digits), TextColor, 9); UpdateLabel("FuturePrice", 30, 70, "Future: " + DoubleToString(FuturePrice, _Digits), TextColor, 9); UpdateLabel("Spread", 150, 55, "Spread: " + IntegerToString((int)SymbolInfo.Spread()), TextColor, 9); // Delta Section UpdateLabel("DeltaTitle", 300, 35, "DELTA & DEVIATION", clrYellow, 10); UpdateLabel("DeltaValue", 300, 55, "ΔPrice: " + DoubleToString(DeltaPrice, 4), DeltaPrice > 0 ? clrLime : clrRed, 9); UpdateLabel("DeltaEMA", 300, 70, "ΔEMA: " + DoubleToString(DeltaEMA, 4), TextColor, 9); UpdateLabel("Deviation", 420, 55, "Deviation: " + DoubleToString(DeltaDeviation/_Point, 1) + " pts", MathAbs(DeltaDeviation) > InpDeviationThreshold * _Point ? WarningColor : TextColor, 9); // Levels Section UpdateLabel("LevelsTitle", 570, 35, "OI & KEY LEVELS", clrYellow, 10); UpdateLabel("NearestCALL", 570, 55, "Nearest CALL: " + (nearestCall>0?DoubleToString(nearestCall, _Digits):"N/A"), clrCyan, 9); UpdateLabel("NearestPUT", 570, 70, "Nearest PUT: " + (nearestPut>0?DoubleToString(nearestPut, _Digits):"N/A"), clrCyan, 9); UpdateLabel("DistUpper", 725, 55, "%Far Upper: " + DoubleToString(distToUpper, 2), TextColor, 9); UpdateLabel("DistLower", 725, 70, "%Far Lower: " + DoubleToString(distToLower, 2), TextColor, 9); // Statistics Section UpdateLabel("StatsTitle", 30, 115, "STATISTICS", clrYellow, 10); UpdateLabel("DailyTrades", 30, 135, "Trades Today: " + IntegerToString(DailyTradeCount), TextColor, 9); UpdateLabel("DailyGain", 30, 150, "%Gain: " + DoubleToString(dailyGainPercent, 2), dailyGainPercent >= 0 ? ProfitColor : LossColor, 9); UpdateLabel("MaxDD", 30, 165, "%Max DD: " + DoubleToString(maxDD, 2), maxDD > 5 ? WarningColor : TextColor, 9); UpdateLabel("ConsecLosses", 180, 135, "Consec. Losses: " + IntegerToString(ConsecutiveLosses), ConsecutiveLosses > 0 ? WarningColor : TextColor, 9); UpdateLabel("DailyProfit", 180, 150, "Daily P/L: " + DoubleToString(DailyProfit + DailyLoss, 2), (DailyProfit + DailyLoss) >= 0 ? ProfitColor : LossColor, 9); // Trading Section UpdateLabel("TradingTitle", 450, 115, "TRADING STATUS", clrYellow, 10); UpdateLabel("MarketPhase", 450, 135, "Market: " + marketPhase, phaseColor, 9); UpdateLabel("TradingStatus", 450, 150, "Status: " + (TradingEnabled ? "ACTIVE" : "DISABLED"), TradingEnabled ? clrLime : clrRed, 9); UpdateLabel("ATRPercent", 450, 165, "ATR%: " + DoubleToString(atrPercent, 2), atrPercent > InpMaxATRPercent ? WarningColor : TextColor, 9); // Active Position string posInfo = "No Active Position"; color posColor = clrGray; double posProfit = 0; if(PositionsTotal() > 0) { for(int i = 0; i < PositionsTotal(); i++) { if(PositionInfo.SelectByIndex(i)) { if(PositionInfo.Symbol() == _Symbol && PositionInfo.Magic() == InpMagicNumber) { posInfo = PositionInfo.PositionType() == POSITION_TYPE_BUY ? "LONG (" + DoubleToString(PositionInfo.Volume(), 2) + ")" : "SHORT (" + DoubleToString(PositionInfo.Volume(), 2) + ")"; posColor = PositionInfo.PositionType() == POSITION_TYPE_BUY ? clrLime : clrRed; posProfit = PositionInfo.Profit(); break; } } } } UpdateLabel("PositionInfo", 600, 135, "Position: " + posInfo, posColor, 9); UpdateLabel("PositionProfit", 600, 150, "P/L: " + DoubleToString(posProfit, 2), posProfit >= 0 ? ProfitColor : LossColor, 9); // News Block Warning if(NewsBlockActive) { UpdateLabel("NewsWarning", 600, 165, "NEWS BLOCK ACTIVE", clrRed, 9); } else { ObjectDelete(chart_id, "NewsWarning"); } } //+------------------------------------------------------------------+ //| Update Label | //+------------------------------------------------------------------+ void UpdateLabel(string name, int x, int y, string text, color clr, int fontSize = 9) { if(ObjectFind(chart_id, name) < 0) { ObjectCreate(chart_id, name, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(chart_id, name, OBJPROP_XDISTANCE, x); ObjectSetInteger(chart_id, name, OBJPROP_YDISTANCE, y); ObjectSetInteger(chart_id, name, OBJPROP_COLOR, clr); ObjectSetInteger(chart_id, name, OBJPROP_FONTSIZE, fontSize); ObjectSetString(chart_id, name, OBJPROP_FONT, "Consolas"); ObjectSetInteger(chart_id, name, OBJPROP_BACK, false); ObjectSetInteger(chart_id, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(chart_id, name, OBJPROP_HIDDEN, true); // ตั้งค่า Z-Order ให้สูง ObjectSetInteger(chart_id, name, OBJPROP_ZORDER, 1002); } ObjectSetString(chart_id, name, OBJPROP_TEXT, text); ObjectSetInteger(chart_id, name, OBJPROP_COLOR, clr); } //+------------------------------------------------------------------+ //| Find Nearest Level | //+------------------------------------------------------------------+ double FindNearestLevel(double price, double &levels[], bool findAbove) { double nearest = 0; double minDist = DBL_MAX; for(int i = 0; i < ArraySize(levels); i++) { if(levels[i] <= 0) continue; double dist = MathAbs(price - levels[i]); if(findAbove) { if(levels[i] >= price && dist < minDist) { minDist = dist; nearest = levels[i]; } } else { if(levels[i] <= price && dist < minDist) { minDist = dist; nearest = levels[i]; } } } return nearest; } //+------------------------------------------------------------------+ //| Get Market Phase String | //+------------------------------------------------------------------+ string GetMarketPhaseString() { ENUM_MARKET_PHASE phase = GetMarketPhase(); switch(phase) { case PHASE_BULLISH: return "Bullish"; case PHASE_BEARISH: return "Bearish"; case PHASE_SIDEWAYS: return "Sideways"; default: return "Neutral"; } } //+------------------------------------------------------------------+ //| Get Market Phase Color | //+------------------------------------------------------------------+ color GetMarketPhaseColor() { ENUM_MARKET_PHASE phase = GetMarketPhase(); switch(phase) { case PHASE_BULLISH: return clrLime; case PHASE_BEARISH: return clrRed; case PHASE_SIDEWAYS: return clrYellow; default: return clrGray; } } //+------------------------------------------------------------------+ //| String Split Function | //+------------------------------------------------------------------+ int StringSplit(string text, string delimiter, string &result[]) { int count = 0; int pos = 0; int len = StringLen(delimiter); while(pos < StringLen(text)) { int nextPos = StringFind(text, delimiter, pos); if(nextPos == -1) nextPos = StringLen(text); string substr = StringSubstr(text, pos, nextPos - pos); ArrayResize(result, count + 1); result[count] = substr; count++; pos = nextPos + len; } return count; } //+------------------------------------------------------------------+