diff --git a/OI_MeanReversion_Pro_XAUUSD.mq5 b/OI_MeanReversion_Pro_XAUUSD.mq5 new file mode 100644 index 0000000..1b9d322 --- /dev/null +++ b/OI_MeanReversion_Pro_XAUUSD.mq5 @@ -0,0 +1,2220 @@ +//+------------------------------------------------------------------+ +//| 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_MANUAL; +input string InpOICsvPath = "\\Files\\oi_data.csv"; // Path to CSV file +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; + +//=== 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(); +} + +//+------------------------------------------------------------------+ +//| 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() +{ + string filename = InpOICsvPath; + int filehandle = FileOpen(filename, FILE_READ|FILE_CSV|FILE_ANSI, ','); + + if(filehandle == INVALID_HANDLE) + { + Print("Error opening CSV file: ", filename); + // Fall back to manual levels + InitializeOILevels(); + return; + } + + // Skip header if exists + FileReadString(filehandle); + + int callIndex = 0; + int putIndex = 0; + + while(!FileIsEnding(filehandle) && (callIndex < 3 || putIndex < 3)) + { + string row = FileReadString(filehandle); + 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(type == "CALL" && callIndex < 3) + { + CallLevels[callIndex] = strike; + CallOI[callIndex] = oi; + callIndex++; + } + else if(type == "PUT" && putIndex < 3) + { + PutLevels[putIndex] = strike; + PutOI[putIndex] = oi; + putIndex++; + } + } + } + + FileClose(filehandle); + + // Fill any missing levels with zeros + 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; + } +} + +//+------------------------------------------------------------------+ +//| 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 (manual or from symbol) + 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; +} +//+------------------------------------------------------------------+ \ No newline at end of file diff --git a/OI_MeanReversion_Pro_XAUUSD_A.mq5 b/OI_MeanReversion_Pro_XAUUSD_A.mq5 deleted file mode 100644 index 979b95f..0000000 Binary files a/OI_MeanReversion_Pro_XAUUSD_A.mq5 and /dev/null differ diff --git a/OI_OrderFlow_Absorption_XAUUSD.mq5 b/OI_OrderFlow_Absorption_XAUUSD.mq5 index 160b4f7..96f9df8 100644 --- a/OI_OrderFlow_Absorption_XAUUSD.mq5 +++ b/OI_OrderFlow_Absorption_XAUUSD.mq5 @@ -111,6 +111,8 @@ double SpotPrice = 0.0; double CallLevels[3]; double PutLevels[3]; +int CallOI[3]; +int PutOI[3]; double DynamicFuturePrice = 0.0; double DynamicCallStrike1 = 0.0; @@ -194,17 +196,9 @@ double CurrentBarHigh = 0.0; double CurrentBarLow = 0.0; double LastPrice = 0.0; -double CachedFuturePrice = -1; // -1 = not loaded, 0 = loaded but failed, >0 = success -string LoadedCSVPath = ""; // Path from which CSV was successfully loaded -bool CSVLoadLogged = false; // Track if we've logged the result - -double CSVDynamicCallStrike1 = 0.0; -double CSVDynamicCallStrike2 = 0.0; -double CSVDynamicCallStrike3 = 0.0; -double CSVDynamicPutStrike1 = 0.0; -double CSVDynamicPutStrike2 = 0.0; -double CSVDynamicPutStrike3 = 0.0; -bool CSVStrikesLoaded = false; +double CachedFuturePrice = -1; +string LoadedCSVPath = ""; +bool CSVLoadLogged = false; int OnInit() { Trade.SetExpertMagicNumber(InpMagicNumber); @@ -214,7 +208,7 @@ int OnInit() { SymbolInfo.Name(_Symbol); SymbolInfo.RefreshRates(); - DynamicFuturePrice = InpManualFuturePrice; + DynamicFuturePrice = InpManualFuturePrice; DynamicCallStrike1 = InpCallStrike1; DynamicCallStrike2 = InpCallStrike2; DynamicCallStrike3 = InpCallStrike3; @@ -226,8 +220,7 @@ int OnInit() { InitializeKeyLevels(); if(InpOISource == OI_SOURCE_CSV_FILE) { - LoadFuturePriceFromCSV(); - ApplyCSVStrikeLevels(); + LoadOIFromCSV(); } if(!InitializeIndicators()) { @@ -446,15 +439,18 @@ void DetectAbsorptionFromHistory() { } void UpdateMarketData() { - SpotPrice = SymbolInfo.Bid(); - SymbolInfo.RefreshRates(); - - if(InpOISource == OI_SOURCE_CSV_FILE) { - FuturePrice = LoadFuturePriceFromCSV(); - } else { - FuturePrice = 0; - } -} + SpotPrice = SymbolInfo.Bid(); + SymbolInfo.RefreshRates(); + + if(InpOISource == OI_SOURCE_CSV_FILE) { + if(CachedFuturePrice < 0) { + LoadOIFromCSV(); + } + FuturePrice = CachedFuturePrice; + } else { + FuturePrice = 0; + } + } bool IsPriceNearPutStrike() { double tolerance = InpOIZonePoints * _Point; @@ -940,14 +936,22 @@ void CloseAllPositions() { } void InitializeOILevels() { - CallLevels[0] = DynamicCallStrike1; - CallLevels[1] = DynamicCallStrike2; - CallLevels[2] = DynamicCallStrike3; - - PutLevels[0] = DynamicPutStrike1; - PutLevels[1] = DynamicPutStrike2; - PutLevels[2] = DynamicPutStrike3; -} + CallLevels[0] = DynamicCallStrike1; + CallLevels[1] = DynamicCallStrike2; + CallLevels[2] = DynamicCallStrike3; + + PutLevels[0] = DynamicPutStrike1; + PutLevels[1] = DynamicPutStrike2; + PutLevels[2] = DynamicPutStrike3; + + CallOI[0] = 0; + CallOI[1] = 0; + CallOI[2] = 0; + + PutOI[0] = 0; + PutOI[1] = 0; + PutOI[2] = 0; + } void InitializeKeyLevels() { double allLevels[]; @@ -1001,180 +1005,114 @@ bool InitializeIndicators() { return true; } -double LoadFuturePriceFromCSV() { - if(CachedFuturePrice >= 0 && CachedFuturePrice != 0) { - return CachedFuturePrice; - } +void LoadOIFromCSV() +{ + string paths[]; + int pathCount = 0; - string paths[]; - int pathCount = 0; + ArrayResize(paths, 5); + paths[pathCount++] = InpOICsvPath; + paths[pathCount++] = "oi_data.csv"; + paths[pathCount++] = "\\Files\\oi_data.csv"; + paths[pathCount++] = "..\\oi_scraper\\oi_data.csv"; + paths[pathCount++] = "../oi_scraper/oi_data.csv"; - ArrayResize(paths, 5); - paths[pathCount++] = InpOICsvPath; - paths[pathCount++] = "oi_data.csv"; - paths[pathCount++] = "\\Files\\oi_data.csv"; - paths[pathCount++] = "..\\oi_scraper\\oi_data.csv"; - paths[pathCount++] = "../oi_scraper/oi_data.csv"; + int filehandle = INVALID_HANDLE; + string foundPath = ""; - int filehandle = INVALID_HANDLE; - string foundPath = ""; + for(int i = 0; i < pathCount; i++) { + if(!CSVLoadLogged) { + Print("Trying CSV path: ", paths[i]); + } + filehandle = FileOpen(paths[i], FILE_READ | FILE_CSV | FILE_ANSI, ','); + if(filehandle != INVALID_HANDLE) { + foundPath = paths[i]; + if(!CSVLoadLogged) { + Print("Found CSV file at: ", foundPath); + } + break; + } + } - for(int i = 0; i < pathCount; i++) { - if(!CSVLoadLogged) { - Print("Trying CSV path: ", paths[i]); - } - filehandle = FileOpen(paths[i], FILE_READ | FILE_CSV | FILE_ANSI, ','); - if(filehandle != INVALID_HANDLE) { - foundPath = paths[i]; - if(!CSVLoadLogged) { - Print("Found CSV file at: ", foundPath); - } - break; - } - } + if(filehandle == INVALID_HANDLE) { + if(!CSVLoadLogged) { + Print("CSV ERROR: File not found. Searched paths:"); + for(int i = 0; i < pathCount; i++) { + Print(" - ", paths[i]); + } + CSVLoadLogged = true; + } + return; + } - if(filehandle == INVALID_HANDLE) { - if(!CSVLoadLogged) { - Print("CSV ERROR: File not found. Searched paths:"); - for(int i = 0; i < pathCount; i++) { - Print(" - ", paths[i]); - } - CSVLoadLogged = true; - } - CachedFuturePrice = 0; - return 0.0; - } + int callIndex = 0; + int putIndex = 0; + double futurePrice = 0.0; - double futurePrice = 0.0; - double callStrikes[]; - double putStrikes[]; - int callCount = 0; - int putCount = 0; - bool inPriceSection = false; - int dataLineCount = 0; + while(!FileIsEnding(filehandle) && (callIndex < 3 || putIndex < 3)) + { + string row = FileReadString(filehandle); + string data[]; + int count = StringSplit(row, ',', data); - while(!FileIsEnding(filehandle)) { - string line = FileReadString(filehandle); - dataLineCount++; + if(count >= 3) + { + string type = data[0]; + double strike = StringToDouble(data[1]); + int oi = (int)StringToInteger(data[2]); - if(line == "") { - inPriceSection = true; - continue; - } + 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++; + } + else if(type == "PUT" && putIndex < 3) + { + PutLevels[putIndex] = strike; + PutOI[putIndex] = oi; + putIndex++; + } + } + } - if(dataLineCount == 1 && StringGetCharacter(line, 0) == 0xFEFF) { - line = StringSubstr(line, 1); - if(line == "") { - inPriceSection = true; - continue; - } - } + FileClose(filehandle); - if(StringFind(line, "Type") >= 0 || StringFind(line, "Strike") >= 0 || StringFind(line, "OI") >= 0) { - continue; - } + for(int i = callIndex; i < 3; i++) + { + CallLevels[i] = 0; + CallOI[i] = 0; + } - if(line == "[Price]" || inPriceSection) { - inPriceSection = true; - string parts[]; - int split = StringSplit(line, ',', parts); - - if(split >= 2 && parts[0] == "FuturePrice") { - string priceStr = parts[1]; - while(StringLen(priceStr) > 0 && (StringGetCharacter(priceStr, 0) == 32 || StringGetCharacter(priceStr, 0) == 9)) { - priceStr = StringSubstr(priceStr, 1); - } - while(StringLen(priceStr) > 0 && (StringGetCharacter(priceStr, StringLen(priceStr)-1) == 32 || StringGetCharacter(priceStr, StringLen(priceStr)-1) == 9)) { - priceStr = StringSubstr(priceStr, 0, StringLen(priceStr)-1); - } - futurePrice = StringToDouble(priceStr); - if(!CSVLoadLogged) { - Print("DEBUG: Parsed FuturePrice: ", futurePrice); - } - } - continue; - } + for(int i = putIndex; i < 3; i++) + { + PutLevels[i] = 0; + PutOI[i] = 0; + } - string parts[]; - int split = StringSplit(line, ',', parts); + if(futurePrice > 0) { + CachedFuturePrice = futurePrice; + DynamicFuturePrice = futurePrice; + LoadedCSVPath = foundPath; + CSVLoadLogged = true; + Print("CSV SUCCESS: FuturePrice=", futurePrice, ", CALL=[", CallLevels[0], ",", CallLevels[1], ",", CallLevels[2], "], PUT=[", PutLevels[0], ",", PutLevels[1], ",", PutLevels[2], "] loaded from ", foundPath); + } else { + if(!CSVLoadLogged) { + Print("CSV ERROR: No valid price found in ", foundPath); + CSVLoadLogged = true; + } + CachedFuturePrice = 0; + } - if(split >= 3) { - string typeStr = parts[0]; - string strikeStr = parts[1]; - string oiStr = parts[2]; - - while(StringLen(strikeStr) > 0 && (StringGetCharacter(strikeStr, 0) == 32 || StringGetCharacter(strikeStr, 0) == 9)) { - strikeStr = StringSubstr(strikeStr, 1); - } - while(StringLen(strikeStr) > 0 && (StringGetCharacter(strikeStr, StringLen(strikeStr)-1) == 32 || StringGetCharacter(strikeStr, StringLen(strikeStr)-1) == 9)) { - strikeStr = StringSubstr(strikeStr, 0, StringLen(strikeStr)-1); - } - - double strike = StringToDouble(strikeStr); - if(strike <= 0) continue; - - if(typeStr == "CALL") { - ArrayResize(callStrikes, callCount + 1); - callStrikes[callCount] = strike; - callCount++; - } else if(typeStr == "PUT") { - ArrayResize(putStrikes, putCount + 1); - putStrikes[putCount] = strike; - putCount++; - } - } - } - - FileClose(filehandle); - - if(callCount > 0) { - ArraySort(callStrikes); - if(callCount >= 1) CSVDynamicCallStrike1 = callStrikes[callCount-1]; - if(callCount >= 2) CSVDynamicCallStrike2 = callStrikes[callCount-2]; - if(callCount >= 3) CSVDynamicCallStrike3 = callStrikes[callCount-3]; - } - - if(putCount > 0) { - ArraySort(putStrikes); - if(putCount >= 1) CSVDynamicPutStrike1 = putStrikes[putCount-1]; - if(putCount >= 2) CSVDynamicPutStrike2 = putStrikes[putCount-2]; - if(putCount >= 3) CSVDynamicPutStrike3 = putStrikes[putCount-3]; - } - - if(futurePrice > 0) { - CachedFuturePrice = futurePrice; - LoadedCSVPath = foundPath; - CSVStrikesLoaded = true; - if(!CSVLoadLogged) { - Print("CSV SUCCESS: FuturePrice=", futurePrice, ", CALL=[", CSVDynamicCallStrike1, ",", CSVDynamicCallStrike2, ",", CSVDynamicCallStrike3, "], PUT=[", CSVDynamicPutStrike1, ",", CSVDynamicPutStrike2, ",", CSVDynamicPutStrike3, "] loaded from ", foundPath); - CSVLoadLogged = true; - } - } else { - if(!CSVLoadLogged) { - Print("CSV ERROR: No valid price found in ", foundPath); - Print(" - File exists but contains no parseable price data"); - Print(" - Total lines read: ", dataLineCount); - CSVLoadLogged = true; - } - CachedFuturePrice = 0; - } - - return CachedFuturePrice; - } - - void ApplyCSVStrikeLevels() { - if(!CSVStrikesLoaded) return; - - if(DynamicCallStrike1 == 0 && CSVDynamicCallStrike1 > 0) DynamicCallStrike1 = CSVDynamicCallStrike1; - if(DynamicCallStrike2 == 0 && CSVDynamicCallStrike2 > 0) DynamicCallStrike2 = CSVDynamicCallStrike2; - if(DynamicCallStrike3 == 0 && CSVDynamicCallStrike3 > 0) DynamicCallStrike3 = CSVDynamicCallStrike3; - if(DynamicPutStrike1 == 0 && CSVDynamicPutStrike1 > 0) DynamicPutStrike1 = CSVDynamicPutStrike1; - if(DynamicPutStrike2 == 0 && CSVDynamicPutStrike2 > 0) DynamicPutStrike2 = CSVDynamicPutStrike2; - if(DynamicPutStrike3 == 0 && CSVDynamicPutStrike3 > 0) DynamicPutStrike3 = CSVDynamicPutStrike3; - - InitializeOILevels(); - InitializeKeyLevels(); - } + InitializeKeyLevels(); +} void CheckExistingPositions() { for(int i = 0; i < PositionsTotal(); i++) { @@ -1440,7 +1378,34 @@ void UpdateInputValues() { InitializeKeyLevels(); if(InpOISource == OI_SOURCE_CSV_FILE) { - LoadFuturePriceFromCSV(); - ApplyCSVStrikeLevels(); + CachedFuturePrice = -1; + LoadOIFromCSV(); + } + } + + if(ObjectGetString(chart_id, "CP_CallStrike1", OBJPROP_TEXT, 0, value)) { + DynamicCallStrike1 = StringToDouble(value); + } + if(ObjectGetString(chart_id, "CP_CallStrike2", OBJPROP_TEXT, 0, value)) { + DynamicCallStrike2 = StringToDouble(value); + } + if(ObjectGetString(chart_id, "CP_CallStrike3", OBJPROP_TEXT, 0, value)) { + DynamicCallStrike3 = StringToDouble(value); + } + if(ObjectGetString(chart_id, "CP_PutStrike1", OBJPROP_TEXT, 0, value)) { + DynamicPutStrike1 = StringToDouble(value); + } + if(ObjectGetString(chart_id, "CP_PutStrike2", OBJPROP_TEXT, 0, value)) { + DynamicPutStrike2 = StringToDouble(value); + } + if(ObjectGetString(chart_id, "CP_PutStrike3", OBJPROP_TEXT, 0, value)) { + DynamicPutStrike3 = StringToDouble(value); + } + + InitializeOILevels(); + InitializeKeyLevels(); + + if(InpOISource == OI_SOURCE_CSV_FILE) { + LoadOIFromCSV(); } } \ No newline at end of file diff --git a/oi_scraper/main.py b/oi_scraper/main.py index 2bc346e..51af0eb 100644 --- a/oi_scraper/main.py +++ b/oi_scraper/main.py @@ -304,8 +304,7 @@ def export_to_csv(df, future_price=0.0): for _, row in put_df.iterrows(): f.write(f"PUT,{row['Strike']:.1f},{row['OI']}\n") - f.write("\n[Price]\n") - f.write(f"FuturePrice,{future_price}\n") + f.write(f"Future,{future_price},0\n") logger.info(f"Exported OI data and price to {output_path}")