Add price caching to prevent repeated file reads and improve performance. Implement multi-path search for CSV files with fallback options. Add comprehensive logging for CSV load success/failure states. Update dashboard to display CSV loading status. Simplify scraper CSV output format and automate file transfer to terminal MQL5 Files directory.
4539 lines
163 KiB
Plaintext
4539 lines
163 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
|
|
//| 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 <Trade\Trade.mqh>
|
|
|
|
#include <Trade\AccountInfo.mqh>
|
|
|
|
#include <Trade\SymbolInfo.mqh>
|
|
|
|
#include <Trade\PositionInfo.mqh>
|
|
|
|
#include <Trade\OrderInfo.mqh>
|
|
|
|
#include <Trade\HistoryOrderInfo.mqh>
|
|
|
|
#include <Arrays\ArrayObj.mqh>
|
|
|
|
#include <Arrays\ArrayDouble.mqh>
|
|
|
|
|
|
|
|
//--- 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;
|
|
|
|
//--- CSV Caching
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Load Future Price from CSV file |
|
|
//+------------------------------------------------------------------+
|
|
//+------------------------------------------------------------------+
|
|
//| Load Future Price from CSV |
|
|
//+------------------------------------------------------------------+
|
|
double LoadFuturePriceFromCSV()
|
|
{
|
|
if(CachedFuturePrice >= 0) {
|
|
return CachedFuturePrice;
|
|
}
|
|
|
|
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";
|
|
|
|
int filehandle = INVALID_HANDLE;
|
|
string foundPath = "";
|
|
|
|
for(int i = 0; i < pathCount; i++) {
|
|
filehandle = FileOpen(paths[i], FILE_READ | FILE_CSV | FILE_ANSI, ',');
|
|
if(filehandle != INVALID_HANDLE) {
|
|
foundPath = paths[i];
|
|
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;
|
|
}
|
|
CachedFuturePrice = 0;
|
|
return 0.0;
|
|
}
|
|
|
|
double futurePrice = 0.0;
|
|
|
|
while(!FileIsEnding(filehandle)) {
|
|
string line = FileReadString(filehandle);
|
|
|
|
if(line == "") continue;
|
|
|
|
string parts[];
|
|
int split = StringSplit(line, ',', parts);
|
|
|
|
if(split >= 2) {
|
|
double price = StringToDouble(parts[1]);
|
|
if(price > 0) {
|
|
futurePrice = price;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
FileClose(filehandle);
|
|
|
|
if(futurePrice > 0) {
|
|
CachedFuturePrice = futurePrice;
|
|
LoadedCSVPath = foundPath;
|
|
if(!CSVLoadLogged) {
|
|
Print("CSV SUCCESS: FuturePrice=", futurePrice, " 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");
|
|
CSVLoadLogged = true;
|
|
}
|
|
CachedFuturePrice = 0;
|
|
}
|
|
|
|
return CachedFuturePrice;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Initialize Key Levels |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
void InitializeKeyLevels()
|
|
|
|
{
|
|
|
|
// Calculate key levels based on OI levels
|
|
|
|
if(PutLevels[2] > 0 && CallLevels[2] > 0)
|
|
|
|
{
|
|
|
|
LevelLower2 = PutLevels[2]; // Lowest put strike
|
|
|
|
LevelLower1 = PutLevels[1]; // Middle put strike
|
|
|
|
LevelMid = (PutLevels[0] + CallLevels[0]) / 2; // Mid point
|
|
|
|
LevelUpper1 = CallLevels[1]; // Middle call strike
|
|
|
|
LevelUpper2 = CallLevels[2]; // Highest call strike
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Use current price as reference
|
|
|
|
double currentPrice = SymbolInfo.Bid();
|
|
|
|
LevelLower2 = currentPrice - 500 * _Point;
|
|
|
|
LevelLower1 = currentPrice - 250 * _Point;
|
|
|
|
LevelMid = currentPrice;
|
|
|
|
LevelUpper1 = currentPrice + 250 * _Point;
|
|
|
|
LevelUpper2 = currentPrice + 500 * _Point;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Initialize Indicators |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool InitializeIndicators()
|
|
|
|
{
|
|
|
|
// ATR for volatility
|
|
|
|
ATRHandle = iATR(_Symbol, PERIOD_H1, 14);
|
|
|
|
if(ATRHandle == INVALID_HANDLE)
|
|
|
|
{
|
|
|
|
Print("Error creating ATR indicator");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Moving Averages for trend
|
|
|
|
MAFastHandle = iMA(_Symbol, InpTrendTF, InpTrendMAPeriod1, 0, MODE_EMA, PRICE_CLOSE);
|
|
|
|
MASlowHandle = iMA(_Symbol, InpTrendTF, InpTrendMAPeriod2, 0, MODE_SMA, PRICE_CLOSE);
|
|
|
|
|
|
|
|
if(MAFastHandle == INVALID_HANDLE || MASlowHandle == INVALID_HANDLE)
|
|
|
|
{
|
|
|
|
Print("Error creating MA indicators");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// RSI for momentum (optional)
|
|
|
|
RSIMainHandle = iRSI(_Symbol, PERIOD_H1, 14, PRICE_CLOSE);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
//| Update Market Data |
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool UpdateMarketData()
|
|
|
|
{
|
|
|
|
// Get current prices
|
|
|
|
SpotPrice = SymbolInfo.Bid();
|
|
|
|
SymbolInfo.RefreshRates();
|
|
|
|
|
|
|
|
// Get future price (CSV first, then manual, then spot)
|
|
double csvFuturePrice = LoadFuturePriceFromCSV();
|
|
if(csvFuturePrice > 0)
|
|
{
|
|
FuturePrice = csvFuturePrice;
|
|
Print("Using FuturePrice from CSV: ", FuturePrice);
|
|
}
|
|
else if(DynamicFuturePrice > 0)
|
|
{
|
|
FuturePrice = DynamicFuturePrice;
|
|
}
|
|
else if(InpManualFuturePrice > 0)
|
|
{
|
|
FuturePrice = InpManualFuturePrice;
|
|
}
|
|
else
|
|
{
|
|
// Fall back to spot price
|
|
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 CSV loaded
|
|
|
|
if(CachedFuturePrice < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if(CachedFuturePrice == 0) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
//+------------------------------------------------------------------+ |