From 92391f9d1806efefcb0a5f804648e4db7ec674b4 Mon Sep 17 00:00:00 2001 From: Kunthawat Greethong Date: Thu, 8 Jan 2026 12:48:05 +0700 Subject: [PATCH] update code --- OI_MeanReversion_Pro_XAUUSD.mq5 | 2220 ++++++++++++++++++++++++++++ OI_MeanReversion_Pro_XAUUSD_A.mq5 | Bin 160974 -> 0 bytes OI_OrderFlow_Absorption_XAUUSD.mq5 | 353 ++--- oi_scraper/main.py | 3 +- 4 files changed, 2380 insertions(+), 196 deletions(-) create mode 100644 OI_MeanReversion_Pro_XAUUSD.mq5 delete mode 100644 OI_MeanReversion_Pro_XAUUSD_A.mq5 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 979b95f03cd30540b4596afda1183a3d04c11759..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160974 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 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}")