New EA and Indi
This commit is contained in:
370
Buffer EA/Include/LoggingManager.mqh
Normal file
370
Buffer EA/Include/LoggingManager.mqh
Normal file
@@ -0,0 +1,370 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| LoggingManager.mqh |
|
||||
//| Universal Buffer Reader EA v2.0 |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025"
|
||||
#property link ""
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| CLoggingManager - Smart logging with error deduplication |
|
||||
//+------------------------------------------------------------------+
|
||||
class CLoggingManager
|
||||
{
|
||||
private:
|
||||
bool m_enable_debug;
|
||||
string m_session_id;
|
||||
|
||||
// Error tracking for deduplication
|
||||
struct ErrorRecord
|
||||
{
|
||||
int error_code;
|
||||
string error_message;
|
||||
int count;
|
||||
datetime first_seen;
|
||||
datetime last_seen;
|
||||
};
|
||||
|
||||
ErrorRecord m_error_records[];
|
||||
int m_max_error_records;
|
||||
|
||||
public:
|
||||
//+------------------------------------------------------------------+
|
||||
//| Constructor |
|
||||
//+------------------------------------------------------------------+
|
||||
CLoggingManager()
|
||||
{
|
||||
m_enable_debug = false;
|
||||
m_session_id = "";
|
||||
m_max_error_records = 50;
|
||||
ArrayResize(m_error_records, 0);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Destructor |
|
||||
//+------------------------------------------------------------------+
|
||||
~CLoggingManager()
|
||||
{
|
||||
ArrayResize(m_error_records, 0);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetParameters(bool enable_debug)
|
||||
{
|
||||
m_enable_debug = enable_debug;
|
||||
m_session_id = GenerateSessionID();
|
||||
ClearErrorRecords();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Log info message |
|
||||
//+------------------------------------------------------------------+
|
||||
void LogInfo(string message)
|
||||
{
|
||||
Print("[INFO] ", message);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Log warning message |
|
||||
//+------------------------------------------------------------------+
|
||||
void LogWarning(string message)
|
||||
{
|
||||
Print("[WARNING] ", message);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get error description |
|
||||
//+------------------------------------------------------------------+
|
||||
string ErrorDescription(int error_code)
|
||||
{
|
||||
switch(error_code)
|
||||
{
|
||||
case 0: return "No error";
|
||||
case 1: return "No error, but result is unknown";
|
||||
case 2: return "Common error";
|
||||
case 3: return "Invalid parameters";
|
||||
case 4: return "Trade server is busy";
|
||||
case 5: return "Old version of the client terminal";
|
||||
case 6: return "No connection with trade server";
|
||||
case 7: return "Not enough rights";
|
||||
case 8: return "Too frequent requests";
|
||||
case 9: return "Malfunctional trade operation";
|
||||
case 64: return "Account disabled";
|
||||
case 65: return "Invalid account";
|
||||
case 128: return "Trade timeout";
|
||||
case 129: return "Invalid price";
|
||||
case 130: return "Invalid stops";
|
||||
case 131: return "Invalid volume";
|
||||
case 132: return "Market is closed";
|
||||
case 133: return "Trade is disabled";
|
||||
case 134: return "Not enough money";
|
||||
case 135: return "Price changed";
|
||||
case 136: return "No prices";
|
||||
case 137: return "Broker is busy";
|
||||
case 138: return "New prices (requote)";
|
||||
case 139: return "Order locked";
|
||||
case 140: return "Long positions only allowed";
|
||||
case 141: return "Too many requests";
|
||||
case 145: return "Modification denied because order is too close to market";
|
||||
case 146: return "Trade context is busy";
|
||||
case 147: return "Expirations are denied by broker";
|
||||
case 148: return "Too many orders";
|
||||
case 149: return "Hedge is prohibited";
|
||||
case 150: return "Prohibited by FIFO rules";
|
||||
case 4000: return "No error";
|
||||
case 4001: return "Wrong function pointer";
|
||||
case 4002: return "Array index is out of range";
|
||||
case 4003: return "No memory for function call stack";
|
||||
case 4004: return "Recursive stack overflow";
|
||||
case 4005: return "Not enough stack for parameter";
|
||||
case 4006: return "No memory for parameter string";
|
||||
case 4007: return "No memory for temp string";
|
||||
case 4008: return "Not initialized string";
|
||||
case 4009: return "Not initialized arraystring";
|
||||
case 4010: return "No memory for arraystring";
|
||||
case 4011: return "Too long string";
|
||||
case 4012: return "Remainder from zero divide";
|
||||
case 4013: return "Zero divide";
|
||||
case 4014: return "Unknown command";
|
||||
case 4015: return "Wrong jump (never generated error)";
|
||||
case 4016: return "Not initialized array";
|
||||
case 4017: return "DLL calls are not allowed";
|
||||
case 4018: return "Cannot load library";
|
||||
case 4019: return "Cannot call function";
|
||||
case 4020: return "External function calls are not allowed";
|
||||
case 4021: return "Not enough memory for temp string";
|
||||
case 4022: return "System is busy (never generated error)";
|
||||
case 4023: return "Internal error";
|
||||
case 4024: return "Out of memory";
|
||||
case 4025: return "Invalid pointer";
|
||||
case 4026: return "Too long string (up to 256 characters)";
|
||||
case 4027: return "Structures or classes containing objects are not allowed";
|
||||
case 4028: return "Not enough memory for string";
|
||||
case 4029: return "Not enough memory for arraystring";
|
||||
case 4030: return "Not enough memory for array";
|
||||
case 4031: return "Unknown object type";
|
||||
case 4032: return "Invalid object type";
|
||||
case 4033: return "Object is not initialized";
|
||||
case 4034: return "Cannot apply delete operation";
|
||||
case 4035: return "Too many objects";
|
||||
case 4036: return "Cannot create object";
|
||||
case 4037: return "Invalid object pointer";
|
||||
case 4038: return "Too many array dimensions";
|
||||
case 4039: return "Access to arrayindex is out of range";
|
||||
case 4040: return "Custom indicator error";
|
||||
case 4041: return "Incorrect series array using";
|
||||
case 4042: return "Custom indicator error";
|
||||
case 4043: return "Arrays are incompatible";
|
||||
case 4044: return "Series array cannot be used as timeseries";
|
||||
case 4045: return "Custom indicator error";
|
||||
case 4046: return "Internal error";
|
||||
case 4047: return "Custom indicator error";
|
||||
case 4048: return "Internal error";
|
||||
case 4049: return "String error";
|
||||
case 4050: return "String error";
|
||||
case 4051: return "String error";
|
||||
case 4052: return "String error";
|
||||
case 4053: return "String error";
|
||||
case 4054: return "Too long string";
|
||||
case 4055: return "String error";
|
||||
case 4056: return "String error";
|
||||
case 4057: return "String error";
|
||||
case 4058: return "String error";
|
||||
case 4059: return "String error";
|
||||
case 4060: return "String error";
|
||||
case 4061: return "Array error";
|
||||
case 4062: return "Array error";
|
||||
case 4063: return "Array error";
|
||||
case 4064: return "Array error";
|
||||
case 4065: return "Array error";
|
||||
case 4066: return "Array error";
|
||||
case 4067: return "Array error";
|
||||
case 4068: return "Array error";
|
||||
case 4069: return "String error";
|
||||
case 4070: return "String error";
|
||||
case 4071: return "String error";
|
||||
case 4072: return "String error";
|
||||
case 4073: return "String error";
|
||||
case 4074: return "String error";
|
||||
case 4075: return "String error";
|
||||
case 4076: return "String error";
|
||||
case 4077: return "String error";
|
||||
case 4078: return "String error";
|
||||
case 4079: return "String error";
|
||||
case 4080: return "String error";
|
||||
case 4081: return "Too many array dimensions";
|
||||
case 4082: return "Too many array dimensions";
|
||||
case 4083: return "Too many array dimensions";
|
||||
case 4084: return "Too many array dimensions";
|
||||
case 4085: return "Array error";
|
||||
case 4086: return "Array error";
|
||||
case 4087: return "Array error";
|
||||
case 4088: return "Array error";
|
||||
case 4089: return "Array error";
|
||||
case 4090: return "Array error";
|
||||
case 4091: return "Array error";
|
||||
case 4092: return "Array error";
|
||||
case 4093: return "Array error";
|
||||
case 4094: return "Array error";
|
||||
case 4095: return "Array error";
|
||||
case 4096: return "Array error";
|
||||
case 4097: return "Array error";
|
||||
case 4098: return "Array error";
|
||||
case 4099: return "Array error";
|
||||
case 4100: return "Array error";
|
||||
case 4101: return "Array error";
|
||||
case 4102: return "Array error";
|
||||
case 4103: return "Array error";
|
||||
case 4104: return "Array error";
|
||||
case 4105: return "Array error";
|
||||
case 4106: return "Array error";
|
||||
case 4107: return "Array error";
|
||||
case 4108: return "Array error";
|
||||
case 4109: return "Array error";
|
||||
case 4110: return "Array error";
|
||||
case 4111: return "Array error";
|
||||
case 4112: return "Array error";
|
||||
case 4113: return "Array error";
|
||||
case 4114: return "Array error";
|
||||
case 4115: return "Array error";
|
||||
case 4116: return "Array error";
|
||||
case 4117: return "Array error";
|
||||
case 4118: return "Array error";
|
||||
case 4119: return "Array error";
|
||||
case 4200: return "Object is not exist";
|
||||
case 4201: return "Unknown object property";
|
||||
case 4202: return "Object is not exist";
|
||||
case 4203: return "Unknown object type";
|
||||
case 4204: return "No object name";
|
||||
case 4205: return "Object coordinates error";
|
||||
case 4206: return "No specified subwindow";
|
||||
case 4207: return "Some object error";
|
||||
default: return "Unknown error code: " + IntegerToString(error_code);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Log error with deduplication |
|
||||
//+------------------------------------------------------------------+
|
||||
void LogError(int error_code, string context)
|
||||
{
|
||||
string error_message = ErrorDescription(error_code);
|
||||
|
||||
// Check if this error has been logged before
|
||||
if(IsErrorDuplicate(error_code, error_message))
|
||||
{
|
||||
// Update existing record
|
||||
for(int i = 0; i < ArraySize(m_error_records); i++)
|
||||
{
|
||||
if(m_error_records[i].error_code == error_code &&
|
||||
m_error_records[i].error_message == error_message)
|
||||
{
|
||||
m_error_records[i].count++;
|
||||
m_error_records[i].last_seen = TimeCurrent();
|
||||
|
||||
// Log with repetition count
|
||||
Print("[ERROR] ", FormatErrorMessage(error_code, context, m_error_records[i].count));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// New error - add to tracking
|
||||
AddErrorRecord(error_code, error_message);
|
||||
Print("[ERROR] ", FormatErrorMessage(error_code, context, 1));
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Log debug message (only if enabled) |
|
||||
//+------------------------------------------------------------------+
|
||||
void LogDebug(string message)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[DEBUG] ", message);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Clear error records |
|
||||
//+------------------------------------------------------------------+
|
||||
void ClearErrorRecords()
|
||||
{
|
||||
ArrayResize(m_error_records, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if error has been logged before |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsErrorDuplicate(int error_code, string error_message)
|
||||
{
|
||||
for(int i = 0; i < ArraySize(m_error_records); i++)
|
||||
{
|
||||
if(m_error_records[i].error_code == error_code &&
|
||||
m_error_records[i].error_message == error_message)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Add error to tracking |
|
||||
//+------------------------------------------------------------------+
|
||||
void AddErrorRecord(int error_code, string error_message)
|
||||
{
|
||||
int size = ArraySize(m_error_records);
|
||||
|
||||
// Limit number of tracked errors
|
||||
if(size >= m_max_error_records)
|
||||
{
|
||||
// Remove oldest error by shifting elements
|
||||
for(int i = 0; i < size - 1; i++)
|
||||
{
|
||||
m_error_records[i] = m_error_records[i + 1];
|
||||
}
|
||||
size = m_max_error_records - 1;
|
||||
}
|
||||
|
||||
ArrayResize(m_error_records, size + 1);
|
||||
m_error_records[size].error_code = error_code;
|
||||
m_error_records[size].error_message = error_message;
|
||||
m_error_records[size].count = 1;
|
||||
m_error_records[size].first_seen = TimeCurrent();
|
||||
m_error_records[size].last_seen = TimeCurrent();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Format error message for display |
|
||||
//+------------------------------------------------------------------+
|
||||
string FormatErrorMessage(int error_code, string context, int count)
|
||||
{
|
||||
string error_msg = ErrorDescription(error_code);
|
||||
string result = error_msg + " (Code: " + IntegerToString(error_code) + ")";
|
||||
|
||||
if(count > 1)
|
||||
{
|
||||
result += " [REPEATED " + IntegerToString(count) + "x]";
|
||||
}
|
||||
|
||||
result += "\nContext: " + context;
|
||||
return result;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Generate session ID |
|
||||
//+------------------------------------------------------------------+
|
||||
string GenerateSessionID()
|
||||
{
|
||||
datetime now = TimeCurrent();
|
||||
return TimeToString(now, TIME_DATE|TIME_MINUTES|TIME_SECONDS);
|
||||
}
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
444
Buffer EA/Include/MoneyManager.mqh
Normal file
444
Buffer EA/Include/MoneyManager.mqh
Normal file
@@ -0,0 +1,444 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| MoneyManager.mqh |
|
||||
//| Universal Buffer Reader EA v2.0 |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025"
|
||||
#property link ""
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| CMoneyManager - Calculates lot sizes and manages daily profit |
|
||||
//+------------------------------------------------------------------+
|
||||
class CMoneyManager
|
||||
{
|
||||
private:
|
||||
double m_base_lot_size;
|
||||
bool m_use_percent_balance;
|
||||
double m_percent_of_balance_for_profit;
|
||||
double m_daily_profit_target_percent;
|
||||
|
||||
double m_min_lot;
|
||||
double m_max_lot;
|
||||
double m_lot_step;
|
||||
double m_point_value;
|
||||
double m_tick_value;
|
||||
|
||||
// State
|
||||
double m_daily_profit_accumulated;
|
||||
double m_daily_start_balance;
|
||||
|
||||
// Logging
|
||||
bool m_enable_debug;
|
||||
|
||||
public:
|
||||
//+------------------------------------------------------------------+
|
||||
//| Constructor |
|
||||
//+------------------------------------------------------------------+
|
||||
CMoneyManager()
|
||||
{
|
||||
m_base_lot_size = 0.03;
|
||||
m_use_percent_balance = true;
|
||||
m_percent_of_balance_for_profit = 1.0;
|
||||
m_daily_profit_target_percent = 2.0;
|
||||
|
||||
m_min_lot = 0.01;
|
||||
m_max_lot = 100.0;
|
||||
m_lot_step = 0.01;
|
||||
m_point_value = 0;
|
||||
m_tick_value = 0;
|
||||
|
||||
m_daily_profit_accumulated = 0;
|
||||
m_daily_start_balance = 0;
|
||||
m_enable_debug = false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Destructor |
|
||||
//+------------------------------------------------------------------+
|
||||
~CMoneyManager()
|
||||
{
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetParameters(
|
||||
double base_lot_size,
|
||||
bool use_percent_balance, double percent_of_balance_for_profit,
|
||||
double daily_profit_target_percent,
|
||||
double min_lot, double max_lot, double lot_step,
|
||||
double point_value, double tick_value,
|
||||
bool enable_debug = false
|
||||
)
|
||||
{
|
||||
// Validate parameters
|
||||
if(base_lot_size <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid base lot size: ", base_lot_size, ". Using default 0.01");
|
||||
m_base_lot_size = 0.01;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_base_lot_size = base_lot_size;
|
||||
}
|
||||
|
||||
if(percent_of_balance_for_profit <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid percent of balance for profit: ", percent_of_balance_for_profit, ". Using default 1.0");
|
||||
m_percent_of_balance_for_profit = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_percent_of_balance_for_profit = percent_of_balance_for_profit;
|
||||
}
|
||||
|
||||
if(daily_profit_target_percent < 0)
|
||||
{
|
||||
Print("[ERROR] Invalid daily profit target percent: ", daily_profit_target_percent, ". Using default 2.0");
|
||||
m_daily_profit_target_percent = 2.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_daily_profit_target_percent = daily_profit_target_percent;
|
||||
}
|
||||
|
||||
if(min_lot <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid min lot: ", min_lot, ". Using default 0.01");
|
||||
m_min_lot = 0.01;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_min_lot = min_lot;
|
||||
}
|
||||
|
||||
if(max_lot <= 0 || max_lot < min_lot)
|
||||
{
|
||||
Print("[ERROR] Invalid max lot: ", max_lot, ". Using default 100.0");
|
||||
m_max_lot = 100.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_max_lot = max_lot;
|
||||
}
|
||||
|
||||
if(lot_step <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid lot step: ", lot_step, ". Using default 0.01");
|
||||
m_lot_step = 0.01;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lot_step = lot_step;
|
||||
}
|
||||
|
||||
if(point_value <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid point value: ", point_value);
|
||||
}
|
||||
m_point_value = point_value;
|
||||
|
||||
if(tick_value <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid tick value: ", tick_value);
|
||||
}
|
||||
m_tick_value = tick_value;
|
||||
|
||||
m_use_percent_balance = use_percent_balance;
|
||||
m_enable_debug = enable_debug;
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[MoneyManager] Parameters set:");
|
||||
Print(" Base lot size: ", m_base_lot_size);
|
||||
Print(" Use % balance: ", m_use_percent_balance);
|
||||
Print(" % of balance for profit: ", m_percent_of_balance_for_profit);
|
||||
Print(" Daily profit target %: ", m_daily_profit_target_percent);
|
||||
Print(" Min lot: ", m_min_lot, ", Max lot: ", m_max_lot, ", Lot step: ", m_lot_step);
|
||||
Print(" Point value: ", m_point_value, ", Tick value: ", m_tick_value);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set debug mode |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetDebugMode(bool enable_debug)
|
||||
{
|
||||
m_enable_debug = enable_debug;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate lot size (pure function) |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateLotSize(
|
||||
bool is_buy,
|
||||
double open_price,
|
||||
double tp_price,
|
||||
double account_balance
|
||||
)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[MoneyManager] Calculating lot size...");
|
||||
Print(" Direction: ", (is_buy ? "BUY" : "SELL"));
|
||||
Print(" Open price: ", open_price);
|
||||
Print(" TP price: ", tp_price);
|
||||
Print(" Account balance: ", account_balance);
|
||||
}
|
||||
|
||||
// Validate inputs
|
||||
if(open_price <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid open price: ", open_price);
|
||||
return m_base_lot_size;
|
||||
}
|
||||
|
||||
if(tp_price <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid TP price: ", tp_price);
|
||||
return m_base_lot_size;
|
||||
}
|
||||
|
||||
if(account_balance <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid account balance: ", account_balance);
|
||||
return m_base_lot_size;
|
||||
}
|
||||
|
||||
// Calculate TP points
|
||||
double tp_points = 0;
|
||||
if(is_buy)
|
||||
{
|
||||
tp_points = (tp_price - open_price) / m_point_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
tp_points = (open_price - tp_price) / m_point_value;
|
||||
}
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print(" TP points: ", tp_points);
|
||||
}
|
||||
|
||||
if(tp_points <= 0)
|
||||
{
|
||||
Print("[WARNING] TP points <= 0. Using base lot size: ", m_base_lot_size);
|
||||
return m_base_lot_size;
|
||||
}
|
||||
|
||||
// Calculate base lot
|
||||
double base_lot = m_base_lot_size;
|
||||
if(m_use_percent_balance)
|
||||
{
|
||||
base_lot = CalculateBaseLot(tp_points, account_balance);
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print(" Base lot from % balance: ", base_lot);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print(" Using fixed base lot: ", base_lot);
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize and return
|
||||
double normalized_lot = NormalizeLotSize(base_lot);
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print(" Normalized lot: ", normalized_lot);
|
||||
Print("[MoneyManager] Lot size calculation complete: ", normalized_lot);
|
||||
}
|
||||
|
||||
return normalized_lot;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Reset daily profit tracking |
|
||||
//+------------------------------------------------------------------+
|
||||
void ResetDailyProfit(double current_balance)
|
||||
{
|
||||
if(current_balance <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid current balance for daily profit reset: ", current_balance);
|
||||
return;
|
||||
}
|
||||
|
||||
m_daily_start_balance = current_balance;
|
||||
m_daily_profit_accumulated = 0;
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[MoneyManager] Daily profit tracking reset");
|
||||
Print(" Start balance: ", m_daily_start_balance);
|
||||
Print(" Target profit: ", GetDailyProfitTarget());
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if daily profit target reached |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsDailyProfitTargetReached()
|
||||
{
|
||||
if(m_daily_profit_target_percent <= 0)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[MoneyManager] Daily profit target disabled (0%)");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
double target_profit = GetDailyProfitTarget();
|
||||
bool reached = (m_daily_profit_accumulated >= target_profit);
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[MoneyManager] Daily profit check:");
|
||||
Print(" Accumulated: ", m_daily_profit_accumulated);
|
||||
Print(" Target: ", target_profit);
|
||||
Print(" Reached: ", (reached ? "YES" : "NO"));
|
||||
}
|
||||
|
||||
return reached;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get daily profit target |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetDailyProfitTarget()
|
||||
{
|
||||
if(m_daily_start_balance <= 0) return 0;
|
||||
return m_daily_start_balance * m_daily_profit_target_percent / 100.0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get daily profit accumulated |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetDailyProfitAccumulated()
|
||||
{
|
||||
return m_daily_profit_accumulated;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get daily profit percentage |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetDailyProfitPercent()
|
||||
{
|
||||
if(m_daily_start_balance <= 0) return 0;
|
||||
return (m_daily_profit_accumulated / m_daily_start_balance) * 100.0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set daily profit accumulated (for tracking) |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetDailyProfitAccumulated(double value)
|
||||
{
|
||||
m_daily_profit_accumulated = value;
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[MoneyManager] Daily profit accumulated set to: ", value);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate base lot based on % balance |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateBaseLot(double tp_points, double account_balance)
|
||||
{
|
||||
if(tp_points <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid TP points for base lot calculation: ", tp_points);
|
||||
return m_base_lot_size;
|
||||
}
|
||||
|
||||
if(m_tick_value <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid tick value for base lot calculation: ", m_tick_value);
|
||||
return m_base_lot_size;
|
||||
}
|
||||
|
||||
if(account_balance <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid account balance for base lot calculation: ", account_balance);
|
||||
return m_base_lot_size;
|
||||
}
|
||||
|
||||
double target_profit = account_balance * (m_percent_of_balance_for_profit / 100.0);
|
||||
double profit_per_lot = tp_points * m_tick_value;
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[MoneyManager] Base lot calculation:");
|
||||
Print(" Target profit: ", target_profit, " (", m_percent_of_balance_for_profit, "% of balance)");
|
||||
Print(" Profit per lot: ", profit_per_lot);
|
||||
}
|
||||
|
||||
if(profit_per_lot <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid profit per lot: ", profit_per_lot);
|
||||
return m_base_lot_size;
|
||||
}
|
||||
|
||||
double lot = target_profit / profit_per_lot;
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print(" Calculated lot: ", lot);
|
||||
}
|
||||
|
||||
return lot;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Normalize lot size to broker requirements |
|
||||
//+------------------------------------------------------------------+
|
||||
double NormalizeLotSize(double lot)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[MoneyManager] Normalizing lot size: ", lot);
|
||||
}
|
||||
|
||||
// Round to lot step
|
||||
double rounded_lot = MathFloor(lot / m_lot_step) * m_lot_step;
|
||||
|
||||
if(m_enable_debug && rounded_lot != lot)
|
||||
{
|
||||
Print(" Rounded to lot step: ", rounded_lot, " (step: ", m_lot_step, ")");
|
||||
}
|
||||
|
||||
// Ensure within min/max
|
||||
double min_adjusted = MathMax(rounded_lot, m_min_lot);
|
||||
double max_adjusted = MathMin(min_adjusted, m_max_lot);
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
if(min_adjusted != rounded_lot)
|
||||
{
|
||||
Print(" Adjusted to min lot: ", min_adjusted, " (min: ", m_min_lot, ")");
|
||||
}
|
||||
if(max_adjusted != min_adjusted)
|
||||
{
|
||||
Print(" Adjusted to max lot: ", max_adjusted, " (max: ", m_max_lot, ")");
|
||||
}
|
||||
}
|
||||
|
||||
double normalized = NormalizeDouble(max_adjusted, 2);
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print(" Final normalized lot: ", normalized);
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
531
Buffer EA/Include/PartialCloseManager.mqh
Normal file
531
Buffer EA/Include/PartialCloseManager.mqh
Normal file
@@ -0,0 +1,531 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| PartialCloseManager.mqh |
|
||||
//| Universal Buffer Reader EA v2.0 |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025"
|
||||
#property link ""
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| CPartialCloseManager - Manages multiple TPs and partial closes |
|
||||
//+------------------------------------------------------------------+
|
||||
class CPartialCloseManager
|
||||
{
|
||||
private:
|
||||
bool m_enable_partial_close;
|
||||
bool m_use_equal_division;
|
||||
double m_partial_close_percentages[];
|
||||
|
||||
int m_magic_number;
|
||||
string m_symbol;
|
||||
CTrade *m_trade;
|
||||
|
||||
// TP tracking to prevent duplicate closes
|
||||
struct TPTracking
|
||||
{
|
||||
ulong ticket;
|
||||
int last_closed_tp;
|
||||
datetime last_check_time;
|
||||
};
|
||||
|
||||
TPTracking m_tp_tracking[];
|
||||
int m_max_tracking_records;
|
||||
|
||||
// Logging
|
||||
bool m_enable_debug;
|
||||
|
||||
public:
|
||||
//+------------------------------------------------------------------+
|
||||
//| TP lot allocation structure |
|
||||
//+------------------------------------------------------------------+
|
||||
struct TPLotAllocation
|
||||
{
|
||||
double lots;
|
||||
double percentage;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Constructor |
|
||||
//+------------------------------------------------------------------+
|
||||
CPartialCloseManager()
|
||||
{
|
||||
m_enable_partial_close = true;
|
||||
m_use_equal_division = true;
|
||||
|
||||
ArrayResize(m_partial_close_percentages, 0);
|
||||
|
||||
m_magic_number = 0;
|
||||
m_symbol = "";
|
||||
m_enable_debug = false;
|
||||
m_max_tracking_records = 100;
|
||||
|
||||
ArrayResize(m_tp_tracking, 0);
|
||||
|
||||
m_trade = new CTrade();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Destructor |
|
||||
//+------------------------------------------------------------------+
|
||||
~CPartialCloseManager()
|
||||
{
|
||||
if(m_trade != NULL)
|
||||
{
|
||||
delete m_trade;
|
||||
m_trade = NULL;
|
||||
}
|
||||
|
||||
ArrayResize(m_partial_close_percentages, 0);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetParameters(
|
||||
bool enable_partial_close,
|
||||
bool use_equal_division,
|
||||
double &partial_close_percentages[],
|
||||
int magic_number,
|
||||
string symbol,
|
||||
bool enable_debug = false
|
||||
)
|
||||
{
|
||||
m_enable_partial_close = enable_partial_close;
|
||||
m_use_equal_division = use_equal_division;
|
||||
|
||||
// Validate and copy percentages
|
||||
int pct_count = ArraySize(partial_close_percentages);
|
||||
ArrayResize(m_partial_close_percentages, pct_count);
|
||||
|
||||
double total_pct = 0;
|
||||
for(int i = 0; i < pct_count; i++)
|
||||
{
|
||||
if(partial_close_percentages[i] <= 0)
|
||||
{
|
||||
Print("[WARNING] Invalid partial close percentage at index ", i, ": ",
|
||||
partial_close_percentages[i], ". Using 0%");
|
||||
m_partial_close_percentages[i] = 0;
|
||||
}
|
||||
else if(partial_close_percentages[i] > 100)
|
||||
{
|
||||
Print("[WARNING] Partial close percentage > 100% at index ", i, ": ",
|
||||
partial_close_percentages[i], ". Using 100%");
|
||||
m_partial_close_percentages[i] = 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_partial_close_percentages[i] = partial_close_percentages[i];
|
||||
}
|
||||
|
||||
total_pct += m_partial_close_percentages[i];
|
||||
}
|
||||
|
||||
if(!m_use_equal_division && MathAbs(total_pct - 100.0) > 0.01)
|
||||
{
|
||||
Print("[WARNING] Partial close percentages don't sum to 100%: ", total_pct, "%");
|
||||
}
|
||||
|
||||
m_magic_number = magic_number;
|
||||
m_symbol = symbol;
|
||||
m_enable_debug = enable_debug;
|
||||
|
||||
m_trade.SetExpertMagicNumber(m_magic_number);
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[PartialCloseManager] Parameters set:");
|
||||
Print(" Enable partial close: ", m_enable_partial_close);
|
||||
Print(" Use equal division: ", m_use_equal_division);
|
||||
Print(" Partial close percentages: ", pct_count, " levels");
|
||||
if(!m_use_equal_division)
|
||||
{
|
||||
for(int i = 0; i < pct_count; i++)
|
||||
{
|
||||
Print(" TP", (i + 1), ": ", m_partial_close_percentages[i], "%");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set debug mode |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetDebugMode(bool enable_debug)
|
||||
{
|
||||
m_enable_debug = enable_debug;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if any TP has been reached and execute partial close |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckAndExecutePartialCloses()
|
||||
{
|
||||
if(!m_enable_partial_close)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[PartialCloseManager] Partial close disabled");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate through all positions
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(!PositionSelectByTicket(PositionGetTicket(i))) continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) != m_symbol) continue;
|
||||
if(PositionGetInteger(POSITION_MAGIC) != m_magic_number) continue;
|
||||
|
||||
ulong ticket = PositionGetInteger(POSITION_TICKET);
|
||||
double total_lots = PositionGetDouble(POSITION_VOLUME);
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[PartialCloseManager] Checking position #", ticket, " (", total_lots, " lots)");
|
||||
}
|
||||
|
||||
// Get TP prices from comment
|
||||
double tp_prices[];
|
||||
int tp_count = 0;
|
||||
if(!GetTPPricesFromComment(ticket, tp_prices, tp_count))
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[PartialCloseManager] No TPs found in comment for ticket #", ticket);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[PartialCloseManager] Found ", tp_count, " TP levels for ticket #", ticket);
|
||||
}
|
||||
|
||||
// Get last closed TP for this ticket
|
||||
int last_closed_tp = GetLastClosedTP(ticket);
|
||||
|
||||
// Check each TP level
|
||||
for(int tp_index = last_closed_tp; tp_index < tp_count; tp_index++)
|
||||
{
|
||||
if(IsTPReached(ticket, tp_index, tp_prices))
|
||||
{
|
||||
// Calculate close lots
|
||||
TPLotAllocation allocation = CalculateTPLotAllocation(total_lots, tp_index, tp_count);
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[PartialCloseManager] TP", (tp_index + 1), " reached for ticket #", ticket);
|
||||
Print(" Close lots: ", allocation.lots, " (", allocation.percentage, "%)");
|
||||
}
|
||||
|
||||
// Execute partial close
|
||||
if(allocation.lots > 0)
|
||||
{
|
||||
if(ExecutePartialClose(ticket, allocation.lots, tp_index))
|
||||
{
|
||||
// Update tracking
|
||||
UpdateTPTracking(ticket, tp_index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate lot size for each TP level |
|
||||
//+------------------------------------------------------------------+
|
||||
TPLotAllocation CalculateTPLotAllocation(double total_lots, int tp_index, int total_tp_count)
|
||||
{
|
||||
TPLotAllocation result;
|
||||
result.lots = 0;
|
||||
result.percentage = 0;
|
||||
|
||||
if(m_use_equal_division)
|
||||
{
|
||||
result.lots = CalculateEqualDivisionLots(total_lots, tp_index, total_tp_count);
|
||||
result.percentage = 100.0 / total_tp_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.lots = CalculateCustomPercentageLots(total_lots, tp_index);
|
||||
if(tp_index < ArraySize(m_partial_close_percentages))
|
||||
{
|
||||
result.percentage = m_partial_close_percentages[tp_index];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get TP prices from position comment |
|
||||
//+------------------------------------------------------------------+
|
||||
bool GetTPPricesFromComment(ulong ticket, double &tp_prices[], int &tp_count)
|
||||
{
|
||||
if(!PositionSelectByTicket(ticket)) return false;
|
||||
|
||||
string comment = PositionGetString(POSITION_COMMENT);
|
||||
tp_count = 0;
|
||||
ArrayResize(tp_prices, 0);
|
||||
|
||||
// Parse comment for TP values
|
||||
// Format: "UnivBufEA_24680_H1;TP1=1.2530;TP2=1.2560;TP3=1.2600;..."
|
||||
|
||||
int tp_index = 1;
|
||||
while(true)
|
||||
{
|
||||
string search_str = ";TP" + IntegerToString(tp_index) + "=";
|
||||
int pos = StringFind(comment, search_str);
|
||||
|
||||
if(pos == -1) break;
|
||||
|
||||
// Extract TP value
|
||||
int start_pos = pos + StringLen(search_str);
|
||||
int end_pos = StringFind(comment, ";", start_pos);
|
||||
|
||||
if(end_pos == -1) end_pos = StringLen(comment);
|
||||
|
||||
string tp_str = StringSubstr(comment, start_pos, end_pos - start_pos);
|
||||
double tp_value = StringToDouble(tp_str);
|
||||
|
||||
ArrayResize(tp_prices, tp_count + 1);
|
||||
tp_prices[tp_count] = tp_value;
|
||||
tp_count++;
|
||||
|
||||
tp_index++;
|
||||
}
|
||||
|
||||
return (tp_count > 0);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if specific TP level has been reached |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsTPReached(ulong ticket, int tp_index, double &tp_prices[])
|
||||
{
|
||||
if(!PositionSelectByTicket(ticket)) return false;
|
||||
if(tp_index >= ArraySize(tp_prices)) return false;
|
||||
|
||||
double tp_price = tp_prices[tp_index];
|
||||
if(tp_price == 0) return false;
|
||||
|
||||
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
double current_price = (pos_type == POSITION_TYPE_BUY) ?
|
||||
SymbolInfoDouble(m_symbol, SYMBOL_BID) :
|
||||
SymbolInfoDouble(m_symbol, SYMBOL_ASK);
|
||||
|
||||
// Check if TP has been reached
|
||||
if(pos_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
return (current_price >= tp_price);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (current_price <= tp_price);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Execute partial close for specific TP level |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ExecutePartialClose(ulong ticket, double close_lots, int tp_index)
|
||||
{
|
||||
if(!PositionSelectByTicket(ticket))
|
||||
{
|
||||
Print("[ERROR] Failed to select position #", ticket);
|
||||
return false;
|
||||
}
|
||||
|
||||
double current_lots = PositionGetDouble(POSITION_VOLUME);
|
||||
|
||||
if(close_lots <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid close lots: ", close_lots, " for ticket #", ticket);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(close_lots > current_lots)
|
||||
{
|
||||
Print("[WARNING] Close lots (", close_lots, ") > current lots (", current_lots,
|
||||
") for ticket #", ticket, ". Adjusting to current lots.");
|
||||
close_lots = current_lots;
|
||||
}
|
||||
|
||||
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
bool result = false;
|
||||
if(pos_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
result = m_trade.Sell(close_lots, m_symbol, 0, 0, 0, "Partial Close TP" + IntegerToString(tp_index + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
result = m_trade.Buy(close_lots, m_symbol, 0, 0, 0, "Partial Close TP" + IntegerToString(tp_index + 1));
|
||||
}
|
||||
|
||||
if(result)
|
||||
{
|
||||
Print("[PartialCloseManager] Partial close executed: ", close_lots, " lots at TP", (tp_index + 1),
|
||||
" for ticket #", ticket);
|
||||
UpdateCommentAfterPartialClose(ticket, tp_index + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("[ERROR] Failed to execute partial close for ticket #", ticket,
|
||||
". Error: ", m_trade.ResultRetcodeDescription());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update position comment after partial close |
|
||||
//+------------------------------------------------------------------+
|
||||
bool UpdateCommentAfterPartialClose(ulong ticket, int tp_index)
|
||||
{
|
||||
if(!PositionSelectByTicket(ticket)) return false;
|
||||
|
||||
string comment = PositionGetString(POSITION_COMMENT);
|
||||
|
||||
// Update BE flag if TP1 was closed
|
||||
if(tp_index == 1)
|
||||
{
|
||||
// Replace BE=0 with BE=1
|
||||
string be_old = ";BE=0";
|
||||
string be_new = ";BE=1";
|
||||
comment = StringReplace(comment, be_old, be_new);
|
||||
}
|
||||
|
||||
// Update TS flag if all TPs closed
|
||||
// Check if this was the last TP
|
||||
double tp_prices[];
|
||||
int tp_count = 0;
|
||||
if(GetTPPricesFromComment(ticket, tp_prices, tp_count))
|
||||
{
|
||||
if(tp_index >= tp_count)
|
||||
{
|
||||
// All TPs closed, activate trailing
|
||||
string ts_old = ";TS=0";
|
||||
string ts_new = ";TS=ACTIVE";
|
||||
comment = StringReplace(comment, ts_old, ts_new);
|
||||
}
|
||||
}
|
||||
|
||||
// Update comment
|
||||
if(m_trade.PositionModify(ticket, PositionGetDouble(POSITION_SL), PositionGetDouble(POSITION_TP)))
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[PartialCloseManager] Comment updated for ticket #", ticket, ": ", comment);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("[ERROR] Failed to update comment for ticket #", ticket);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get last closed TP for ticket |
|
||||
//+------------------------------------------------------------------+
|
||||
int GetLastClosedTP(ulong ticket)
|
||||
{
|
||||
for(int i = 0; i < ArraySize(m_tp_tracking); i++)
|
||||
{
|
||||
if(m_tp_tracking[i].ticket == ticket)
|
||||
{
|
||||
return m_tp_tracking[i].last_closed_tp;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update TP tracking |
|
||||
//+------------------------------------------------------------------+
|
||||
void UpdateTPTracking(ulong ticket, int tp_level)
|
||||
{
|
||||
// Check if ticket already exists in tracking
|
||||
for(int i = 0; i < ArraySize(m_tp_tracking); i++)
|
||||
{
|
||||
if(m_tp_tracking[i].ticket == ticket)
|
||||
{
|
||||
m_tp_tracking[i].last_closed_tp = tp_level;
|
||||
m_tp_tracking[i].last_check_time = TimeCurrent();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add new tracking record
|
||||
int size = ArraySize(m_tp_tracking);
|
||||
if(size >= m_max_tracking_records)
|
||||
{
|
||||
// Remove oldest record
|
||||
ArrayCopy(m_tp_tracking, m_tp_tracking, 0, 1, size - 1);
|
||||
size = m_max_tracking_records - 1;
|
||||
}
|
||||
|
||||
ArrayResize(m_tp_tracking, size + 1);
|
||||
m_tp_tracking[size].ticket = ticket;
|
||||
m_tp_tracking[size].last_closed_tp = tp_level;
|
||||
m_tp_tracking[size].last_check_time = TimeCurrent();
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[PartialCloseManager] Added tracking for ticket #", ticket, ", TP level: ", tp_level);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Clear TP tracking for ticket |
|
||||
//+------------------------------------------------------------------+
|
||||
void ClearTPTracking(ulong ticket)
|
||||
{
|
||||
for(int i = 0; i < ArraySize(m_tp_tracking); i++)
|
||||
{
|
||||
if(m_tp_tracking[i].ticket == ticket)
|
||||
{
|
||||
ArrayCopy(m_tp_tracking, m_tp_tracking, i, i + 1, ArraySize(m_tp_tracking) - i - 1);
|
||||
ArrayResize(m_tp_tracking, ArraySize(m_tp_tracking) - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate equal division lots |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateEqualDivisionLots(double total_lots, int tp_index, int total_tp_count)
|
||||
{
|
||||
if(total_tp_count == 0) return 0;
|
||||
|
||||
double equal_lots = total_lots / total_tp_count;
|
||||
|
||||
// Last TP gets remaining lots
|
||||
if(tp_index == total_tp_count - 1)
|
||||
{
|
||||
return total_lots - (equal_lots * (total_tp_count - 1));
|
||||
}
|
||||
|
||||
return equal_lots;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate custom percentage lots |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateCustomPercentageLots(double total_lots, int tp_index)
|
||||
{
|
||||
if(tp_index >= ArraySize(m_partial_close_percentages)) return 0;
|
||||
|
||||
double percentage = m_partial_close_percentages[tp_index];
|
||||
return total_lots * (percentage / 100.0);
|
||||
}
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
575
Buffer EA/Include/RiskManager.mqh
Normal file
575
Buffer EA/Include/RiskManager.mqh
Normal file
@@ -0,0 +1,575 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RiskManager.mqh |
|
||||
//| Universal Buffer Reader EA v2.0 |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025"
|
||||
#property link ""
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| CRiskManager - Manages breakeven and trailing stop |
|
||||
//+------------------------------------------------------------------+
|
||||
class CRiskManager
|
||||
{
|
||||
private:
|
||||
bool m_use_trailing_stop;
|
||||
int m_trailing_stop_pips;
|
||||
bool m_use_breakeven;
|
||||
int m_breakeven_pips;
|
||||
|
||||
double m_pip_value;
|
||||
int m_digits;
|
||||
double m_stop_level_points;
|
||||
int m_magic_number;
|
||||
string m_symbol;
|
||||
|
||||
CTrade *m_trade;
|
||||
|
||||
// Logging
|
||||
bool m_enable_debug;
|
||||
|
||||
// Partial close tracking
|
||||
bool m_partial_close_enabled;
|
||||
|
||||
public:
|
||||
//+------------------------------------------------------------------+
|
||||
//| Validated SL/TP structure |
|
||||
//+------------------------------------------------------------------+
|
||||
struct ValidatedSLTP
|
||||
{
|
||||
double sl_price;
|
||||
double tp_prices[];
|
||||
int tp_count;
|
||||
bool is_valid;
|
||||
string error_message;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Constructor |
|
||||
//+------------------------------------------------------------------+
|
||||
CRiskManager()
|
||||
{
|
||||
m_use_trailing_stop = false;
|
||||
m_trailing_stop_pips = 300;
|
||||
m_use_breakeven = true;
|
||||
m_breakeven_pips = 30;
|
||||
|
||||
m_pip_value = 0;
|
||||
m_digits = 0;
|
||||
m_stop_level_points = 0;
|
||||
m_magic_number = 0;
|
||||
m_symbol = "";
|
||||
m_enable_debug = false;
|
||||
m_partial_close_enabled = false;
|
||||
|
||||
m_trade = new CTrade();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Destructor |
|
||||
//+------------------------------------------------------------------+
|
||||
~CRiskManager()
|
||||
{
|
||||
if(m_trade != NULL)
|
||||
{
|
||||
delete m_trade;
|
||||
m_trade = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetParameters(
|
||||
bool use_trailing_stop,
|
||||
int trailing_stop_pips,
|
||||
bool use_breakeven,
|
||||
int breakeven_pips,
|
||||
double pip_value,
|
||||
int digits,
|
||||
double stop_level_points,
|
||||
int magic_number,
|
||||
string symbol,
|
||||
bool enable_debug = false
|
||||
)
|
||||
{
|
||||
// Validate parameters
|
||||
if(trailing_stop_pips <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid trailing stop pips: ", trailing_stop_pips, ". Using default 300");
|
||||
m_trailing_stop_pips = 300;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_trailing_stop_pips = trailing_stop_pips;
|
||||
}
|
||||
|
||||
if(breakeven_pips <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid breakeven pips: ", breakeven_pips, ". Using default 30");
|
||||
m_breakeven_pips = 30;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_breakeven_pips = breakeven_pips;
|
||||
}
|
||||
|
||||
if(pip_value <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid pip value: ", pip_value);
|
||||
}
|
||||
m_pip_value = pip_value;
|
||||
|
||||
if(digits <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid digits: ", digits);
|
||||
}
|
||||
m_digits = digits;
|
||||
|
||||
if(stop_level_points < 0)
|
||||
{
|
||||
Print("[ERROR] Invalid stop level points: ", stop_level_points);
|
||||
}
|
||||
m_stop_level_points = stop_level_points;
|
||||
|
||||
m_use_trailing_stop = use_trailing_stop;
|
||||
m_use_breakeven = use_breakeven;
|
||||
m_magic_number = magic_number;
|
||||
m_symbol = symbol;
|
||||
m_enable_debug = enable_debug;
|
||||
|
||||
m_trade.SetExpertMagicNumber(m_magic_number);
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[RiskManager] Parameters set:");
|
||||
Print(" Use trailing stop: ", m_use_trailing_stop, " (", m_trailing_stop_pips, " pips)");
|
||||
Print(" Use breakeven: ", m_use_breakeven, " (", m_breakeven_pips, " pips)");
|
||||
Print(" Pip value: ", m_pip_value, ", Digits: ", m_digits);
|
||||
Print(" Stop level: ", m_stop_level_points, " points");
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set debug mode |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetDebugMode(bool enable_debug)
|
||||
{
|
||||
m_enable_debug = enable_debug;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Enable partial close (for TP-based trailing) |
|
||||
//+------------------------------------------------------------------+
|
||||
void EnablePartialClose(bool enable)
|
||||
{
|
||||
m_partial_close_enabled = enable;
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[RiskManager] Partial close ", (enable ? "enabled" : "disabled"));
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Manage breakeven and trailing stop |
|
||||
//+------------------------------------------------------------------+
|
||||
void ManageRiskManagement()
|
||||
{
|
||||
// Iterate through all positions
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(!PositionSelectByTicket(PositionGetTicket(i))) continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) != m_symbol) continue;
|
||||
if(PositionGetInteger(POSITION_MAGIC) != m_magic_number) continue;
|
||||
|
||||
ulong ticket = PositionGetInteger(POSITION_TICKET);
|
||||
double open_price = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
double current_sl = PositionGetDouble(POSITION_SL);
|
||||
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
bool is_buy = (pos_type == POSITION_TYPE_BUY);
|
||||
double current_price = is_buy ?
|
||||
SymbolInfoDouble(m_symbol, SYMBOL_BID) :
|
||||
SymbolInfoDouble(m_symbol, SYMBOL_ASK);
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[RiskManager] Checking position #", ticket);
|
||||
Print(" Type: ", (is_buy ? "BUY" : "SELL"));
|
||||
Print(" Open: ", open_price, ", Current: ", current_price, ", SL: ", current_sl);
|
||||
}
|
||||
|
||||
// Check breakeven
|
||||
if(m_use_breakeven && current_sl != open_price)
|
||||
{
|
||||
if(ShouldMoveToBreakeven(ticket, open_price, current_price))
|
||||
{
|
||||
TryMoveToBreakeven(ticket, open_price);
|
||||
}
|
||||
}
|
||||
|
||||
// Check TP-based trailing
|
||||
if(m_partial_close_enabled) // Only if partial close is enabled
|
||||
{
|
||||
ManageTPBasedTrailing(ticket, is_buy, open_price, current_price, current_sl);
|
||||
}
|
||||
|
||||
// Check standard trailing
|
||||
if(m_use_trailing_stop)
|
||||
{
|
||||
ManageStandardTrailing(ticket, is_buy, open_price, current_price, current_sl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Validate SL/TP to meet broker requirements |
|
||||
//+------------------------------------------------------------------+
|
||||
ValidatedSLTP ValidateSLTP(
|
||||
bool is_buy,
|
||||
double open_price,
|
||||
double sl_price,
|
||||
double &tp_prices[],
|
||||
int tp_count
|
||||
)
|
||||
{
|
||||
ValidatedSLTP result;
|
||||
result.sl_price = sl_price;
|
||||
result.tp_count = tp_count;
|
||||
result.is_valid = true;
|
||||
result.error_message = "";
|
||||
|
||||
ArrayResize(result.tp_prices, tp_count);
|
||||
ArrayCopy(result.tp_prices, tp_prices);
|
||||
|
||||
// Validate SL
|
||||
if(sl_price != 0)
|
||||
{
|
||||
result.sl_price = AdjustToStopLevel(is_buy, sl_price, open_price);
|
||||
}
|
||||
|
||||
// Validate TPs
|
||||
for(int i = 0; i < tp_count; i++)
|
||||
{
|
||||
if(result.tp_prices[i] != 0)
|
||||
{
|
||||
result.tp_prices[i] = AdjustToStopLevel(is_buy, result.tp_prices[i], open_price);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if SL should move to breakeven |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ShouldMoveToBreakeven(ulong ticket, double open_price, double current_price)
|
||||
{
|
||||
double profit_pips = 0;
|
||||
|
||||
// Get position type
|
||||
if(!PositionSelectByTicket(ticket)) return false;
|
||||
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
if(pos_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
profit_pips = (current_price - open_price) / m_pip_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
profit_pips = (open_price - current_price) / m_pip_value;
|
||||
}
|
||||
|
||||
return (profit_pips >= m_breakeven_pips);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Try to move SL to breakeven |
|
||||
//+------------------------------------------------------------------+
|
||||
bool TryMoveToBreakeven(ulong ticket, double open_price)
|
||||
{
|
||||
if(!PositionSelectByTicket(ticket)) return false;
|
||||
|
||||
double current_sl = PositionGetDouble(POSITION_SL);
|
||||
if(current_sl == open_price) return true; // Already at breakeven
|
||||
|
||||
string comment = PositionGetString(POSITION_COMMENT);
|
||||
|
||||
if(m_trade.PositionModify(ticket, open_price, PositionGetDouble(POSITION_TP)))
|
||||
{
|
||||
Print("[RiskManager] Breakeven set for ticket #", ticket, " at ", open_price);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("[ERROR] Failed to set breakeven for ticket #", ticket,
|
||||
". Error: ", m_trade.ResultRetcodeDescription());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Manage TP-based trailing stop |
|
||||
//+------------------------------------------------------------------+
|
||||
void ManageTPBasedTrailing(ulong ticket, bool is_buy, double open_price, double current_price, double current_sl)
|
||||
{
|
||||
// Get TP prices from comment
|
||||
double tp_prices[];
|
||||
int tp_count = 0;
|
||||
if(!GetTPPricesFromComment(ticket, tp_prices, tp_count)) return;
|
||||
|
||||
if(tp_count == 0) return;
|
||||
|
||||
// Check which TP level has been reached
|
||||
int reached_tp_level = GetReachedTPLevel(is_buy, current_price, tp_prices, tp_count);
|
||||
|
||||
if(reached_tp_level == 0) return; // No TP reached yet
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[RiskManager] TP", reached_tp_level, " reached for ticket #", ticket);
|
||||
}
|
||||
|
||||
// Determine new SL based on TP level
|
||||
double new_sl = 0;
|
||||
|
||||
if(reached_tp_level == 1)
|
||||
{
|
||||
// TP1 reached: Move SL to breakeven
|
||||
new_sl = open_price;
|
||||
}
|
||||
else if(reached_tp_level == 2)
|
||||
{
|
||||
// TP2 reached: Move SL to TP1
|
||||
new_sl = tp_prices[0];
|
||||
}
|
||||
else if(reached_tp_level >= 3)
|
||||
{
|
||||
// TP3 reached: Move SL to TP2
|
||||
new_sl = tp_prices[1];
|
||||
}
|
||||
|
||||
// Check if SL should be moved
|
||||
if(new_sl > 0)
|
||||
{
|
||||
bool should_move = false;
|
||||
if(is_buy)
|
||||
{
|
||||
should_move = (new_sl > current_sl);
|
||||
}
|
||||
else
|
||||
{
|
||||
should_move = (new_sl < current_sl);
|
||||
}
|
||||
|
||||
if(should_move)
|
||||
{
|
||||
TryMoveSL(ticket, new_sl, "TP" + IntegerToString(reached_tp_level));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Manage standard trailing stop |
|
||||
//+------------------------------------------------------------------+
|
||||
void ManageStandardTrailing(ulong ticket, bool is_buy, double open_price, double current_price, double current_sl)
|
||||
{
|
||||
// Calculate new SL based on trailing distance
|
||||
double new_sl = CalculateNewSL(is_buy, current_price);
|
||||
|
||||
if(new_sl == 0) return;
|
||||
|
||||
// Ratchet rule: SL must only move in profit direction
|
||||
bool should_move = false;
|
||||
if(is_buy)
|
||||
{
|
||||
should_move = (new_sl > current_sl);
|
||||
}
|
||||
else
|
||||
{
|
||||
should_move = (new_sl < current_sl);
|
||||
}
|
||||
|
||||
if(should_move)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[RiskManager] Trailing SL for ticket #", ticket);
|
||||
Print(" Current SL: ", current_sl, ", New SL: ", new_sl);
|
||||
}
|
||||
|
||||
TryMoveSL(ticket, new_sl, "TRAIL");
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get TP prices from position comment |
|
||||
//+------------------------------------------------------------------+
|
||||
bool GetTPPricesFromComment(ulong ticket, double &tp_prices[], int &tp_count)
|
||||
{
|
||||
if(!PositionSelectByTicket(ticket)) return false;
|
||||
|
||||
string comment = PositionGetString(POSITION_COMMENT);
|
||||
tp_count = 0;
|
||||
ArrayResize(tp_prices, 0);
|
||||
|
||||
// Parse comment for TP values
|
||||
// Format: "UnivBufEA_24680_H1;TP1=1.2530;TP2=1.2560;TP3=1.2600;..."
|
||||
|
||||
int tp_index = 1;
|
||||
while(true)
|
||||
{
|
||||
string search_str = ";TP" + IntegerToString(tp_index) + "=";
|
||||
int pos = StringFind(comment, search_str);
|
||||
|
||||
if(pos == -1) break;
|
||||
|
||||
// Extract TP value
|
||||
int start_pos = pos + StringLen(search_str);
|
||||
int end_pos = StringFind(comment, ";", start_pos);
|
||||
|
||||
if(end_pos == -1) end_pos = StringLen(comment);
|
||||
|
||||
string tp_str = StringSubstr(comment, start_pos, end_pos - start_pos);
|
||||
double tp_value = StringToDouble(tp_str);
|
||||
|
||||
ArrayResize(tp_prices, tp_count + 1);
|
||||
tp_prices[tp_count] = tp_value;
|
||||
tp_count++;
|
||||
|
||||
tp_index++;
|
||||
}
|
||||
|
||||
return (tp_count > 0);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get reached TP level |
|
||||
//+------------------------------------------------------------------+
|
||||
int GetReachedTPLevel(bool is_buy, double current_price, double &tp_prices[], int tp_count)
|
||||
{
|
||||
for(int i = 0; i < tp_count; i++)
|
||||
{
|
||||
if(tp_prices[i] == 0) continue;
|
||||
|
||||
if(is_buy)
|
||||
{
|
||||
if(current_price >= tp_prices[i]) return (i + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(current_price <= tp_prices[i]) return (i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate new SL for trailing |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateNewSL(bool is_buy, double current_price)
|
||||
{
|
||||
if(m_trailing_stop_pips <= 0) return 0;
|
||||
|
||||
double trailing_distance = m_trailing_stop_pips * m_pip_value;
|
||||
|
||||
if(is_buy)
|
||||
{
|
||||
return NormalizeDouble(current_price - trailing_distance, m_digits);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NormalizeDouble(current_price + trailing_distance, m_digits);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Try to move SL |
|
||||
//+------------------------------------------------------------------+
|
||||
bool TryMoveSL(ulong ticket, double new_sl, string reason)
|
||||
{
|
||||
if(!PositionSelectByTicket(ticket)) return false;
|
||||
|
||||
double current_tp = PositionGetDouble(POSITION_TP);
|
||||
|
||||
if(m_trade.PositionModify(ticket, new_sl, current_tp))
|
||||
{
|
||||
Print("[RiskManager] SL moved for ticket #", ticket, " to ", new_sl, " (", reason, ")");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("[ERROR] Failed to move SL for ticket #", ticket,
|
||||
". Error: ", m_trade.ResultRetcodeDescription());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Adjust price to meet stop level requirements |
|
||||
//+------------------------------------------------------------------+
|
||||
double AdjustToStopLevel(bool is_buy, double price, double open_price)
|
||||
{
|
||||
double distance = 0;
|
||||
|
||||
if(is_buy)
|
||||
{
|
||||
if(price < open_price) // SL
|
||||
{
|
||||
distance = open_price - price;
|
||||
}
|
||||
else // TP
|
||||
{
|
||||
distance = price - open_price;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(price > open_price) // SL
|
||||
{
|
||||
distance = price - open_price;
|
||||
}
|
||||
else // TP
|
||||
{
|
||||
distance = open_price - price;
|
||||
}
|
||||
}
|
||||
|
||||
if(distance < m_stop_level_points)
|
||||
{
|
||||
distance = m_stop_level_points;
|
||||
|
||||
if(is_buy)
|
||||
{
|
||||
if(price < open_price) // SL
|
||||
{
|
||||
price = NormalizeDouble(open_price - distance, m_digits);
|
||||
}
|
||||
else // TP
|
||||
{
|
||||
price = NormalizeDouble(open_price + distance, m_digits);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(price > open_price) // SL
|
||||
{
|
||||
price = NormalizeDouble(open_price + distance, m_digits);
|
||||
}
|
||||
else // TP
|
||||
{
|
||||
price = NormalizeDouble(open_price - distance, m_digits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return price;
|
||||
}
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
574
Buffer EA/Include/SignalDetector.mqh
Normal file
574
Buffer EA/Include/SignalDetector.mqh
Normal file
@@ -0,0 +1,574 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| SignalDetector.mqh |
|
||||
//| Universal Buffer Reader EA v2.0 |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025"
|
||||
#property link ""
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| CSignalDetector - Reads indicator buffers and detects signals |
|
||||
//+------------------------------------------------------------------+
|
||||
class CSignalDetector
|
||||
{
|
||||
private:
|
||||
string m_indicator_name;
|
||||
int m_indicator_handle;
|
||||
|
||||
// Signal buffers
|
||||
int m_buy_signal_buffer;
|
||||
int m_sell_signal_buffer;
|
||||
|
||||
// SL/TP buffers (separate for BUY/SELL)
|
||||
int m_buy_sl_buffer;
|
||||
int m_buy_tp_buffers[];
|
||||
int m_sell_sl_buffer;
|
||||
int m_sell_tp_buffers[];
|
||||
|
||||
// ATR settings
|
||||
int m_sltp_mode;
|
||||
int m_atr_period;
|
||||
int m_atr_handle;
|
||||
double m_sl_atr_multiplier;
|
||||
double m_tp_atr_multiplier;
|
||||
int m_min_tp_pips;
|
||||
|
||||
// Symbol info
|
||||
string m_symbol;
|
||||
double m_pip_value;
|
||||
int m_digits;
|
||||
|
||||
// Logging
|
||||
bool m_enable_debug;
|
||||
|
||||
public:
|
||||
//+------------------------------------------------------------------+
|
||||
//| Signal data structure with multiple TPs |
|
||||
//+------------------------------------------------------------------+
|
||||
struct SignalData
|
||||
{
|
||||
bool has_signal;
|
||||
bool is_buy;
|
||||
double signal_price;
|
||||
double sl_price;
|
||||
double tp_prices[];
|
||||
int tp_count;
|
||||
bool used_atr_fallback;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Constructor |
|
||||
//+------------------------------------------------------------------+
|
||||
CSignalDetector()
|
||||
{
|
||||
m_indicator_name = "";
|
||||
m_indicator_handle = INVALID_HANDLE;
|
||||
m_buy_signal_buffer = 0;
|
||||
m_sell_signal_buffer = 1;
|
||||
m_buy_sl_buffer = 2;
|
||||
m_sell_sl_buffer = 6;
|
||||
m_sltp_mode = 0;
|
||||
m_atr_period = 14;
|
||||
m_atr_handle = INVALID_HANDLE;
|
||||
m_sl_atr_multiplier = 1.5;
|
||||
m_tp_atr_multiplier = 3.0;
|
||||
m_min_tp_pips = 300;
|
||||
m_symbol = "";
|
||||
m_pip_value = 0;
|
||||
m_digits = 0;
|
||||
m_enable_debug = false;
|
||||
|
||||
ArrayResize(m_buy_tp_buffers, 0);
|
||||
ArrayResize(m_sell_tp_buffers, 0);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Destructor |
|
||||
//+------------------------------------------------------------------+
|
||||
~CSignalDetector()
|
||||
{
|
||||
Deinitialize();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Initialize indicator handles |
|
||||
//+------------------------------------------------------------------+
|
||||
bool Initialize()
|
||||
{
|
||||
Print("[SignalDetector] Initializing...");
|
||||
|
||||
// Initialize custom indicator handle
|
||||
if(m_indicator_name != "" && m_indicator_name != "My_Indicator")
|
||||
{
|
||||
m_indicator_handle = iCustom(m_symbol, PERIOD_CURRENT, m_indicator_name);
|
||||
if(m_indicator_handle == INVALID_HANDLE)
|
||||
{
|
||||
int error = GetLastError();
|
||||
Print("[ERROR] Failed to create indicator handle for: ", m_indicator_name,
|
||||
" Error code: ", error);
|
||||
return false;
|
||||
}
|
||||
Print("[SignalDetector] Custom indicator handle created: ", m_indicator_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("[SignalDetector] Using ATR mode only (no custom indicator)");
|
||||
}
|
||||
|
||||
// Initialize ATR handle
|
||||
m_atr_handle = iATR(m_symbol, PERIOD_CURRENT, m_atr_period);
|
||||
if(m_atr_handle == INVALID_HANDLE)
|
||||
{
|
||||
int error = GetLastError();
|
||||
Print("[ERROR] Failed to create ATR handle. Error code: ", error);
|
||||
return false;
|
||||
}
|
||||
Print("[SignalDetector] ATR handle created (Period: ", m_atr_period, ")");
|
||||
|
||||
Print("[SignalDetector] Initialization complete");
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Deinitialize indicator handles |
|
||||
//+------------------------------------------------------------------+
|
||||
void Deinitialize()
|
||||
{
|
||||
Print("[SignalDetector] Deinitializing...");
|
||||
|
||||
if(m_indicator_handle != INVALID_HANDLE)
|
||||
{
|
||||
IndicatorRelease(m_indicator_handle);
|
||||
m_indicator_handle = INVALID_HANDLE;
|
||||
Print("[SignalDetector] Custom indicator handle released");
|
||||
}
|
||||
|
||||
if(m_atr_handle != INVALID_HANDLE)
|
||||
{
|
||||
IndicatorRelease(m_atr_handle);
|
||||
m_atr_handle = INVALID_HANDLE;
|
||||
Print("[SignalDetector] ATR handle released");
|
||||
}
|
||||
|
||||
ArrayResize(m_buy_tp_buffers, 0);
|
||||
ArrayResize(m_sell_tp_buffers, 0);
|
||||
|
||||
Print("[SignalDetector] Deinitialization complete");
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetParameters(
|
||||
string indicator_name,
|
||||
int buy_signal_buffer, int sell_signal_buffer,
|
||||
int buy_sl_buffer, int sell_sl_buffer,
|
||||
int sltp_mode, int atr_period,
|
||||
double sl_atr_multiplier, double tp_atr_multiplier,
|
||||
int min_tp_pips, double pip_value, int digits,
|
||||
string symbol,
|
||||
bool enable_debug = false
|
||||
)
|
||||
{
|
||||
m_indicator_name = indicator_name;
|
||||
m_buy_signal_buffer = buy_signal_buffer;
|
||||
m_sell_signal_buffer = sell_signal_buffer;
|
||||
m_buy_sl_buffer = buy_sl_buffer;
|
||||
m_sell_sl_buffer = sell_sl_buffer;
|
||||
m_sltp_mode = sltp_mode;
|
||||
m_atr_period = atr_period;
|
||||
m_sl_atr_multiplier = sl_atr_multiplier;
|
||||
m_tp_atr_multiplier = tp_atr_multiplier;
|
||||
m_min_tp_pips = min_tp_pips;
|
||||
m_pip_value = pip_value;
|
||||
m_digits = digits;
|
||||
m_symbol = symbol;
|
||||
m_enable_debug = enable_debug;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set debug mode |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetDebugMode(bool enable_debug)
|
||||
{
|
||||
m_enable_debug = enable_debug;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set TP buffers (can have multiple) |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetBuyTPBuffers(int &buffers[])
|
||||
{
|
||||
ArrayCopy(m_buy_tp_buffers, buffers);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set TP buffers (can have multiple) |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetSellTPBuffers(int &buffers[])
|
||||
{
|
||||
ArrayCopy(m_sell_tp_buffers, buffers);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Detect signal from indicator buffers |
|
||||
//+------------------------------------------------------------------+
|
||||
SignalData DetectSignal(int bar_shift = 1)
|
||||
{
|
||||
SignalData result;
|
||||
result.has_signal = false;
|
||||
result.is_buy = false;
|
||||
result.signal_price = 0;
|
||||
result.sl_price = 0;
|
||||
result.tp_count = 0;
|
||||
result.used_atr_fallback = false;
|
||||
ArrayResize(result.tp_prices, 0);
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[SignalDetector] Detecting signal at bar shift: ", bar_shift);
|
||||
}
|
||||
|
||||
// Read buy/sell signal buffers
|
||||
double buy_signal = GetIndicatorBufferValue(m_buy_signal_buffer, bar_shift);
|
||||
double sell_signal = GetIndicatorBufferValue(m_sell_signal_buffer, bar_shift);
|
||||
|
||||
// Determine signal type
|
||||
bool has_buy = (buy_signal != EMPTY_VALUE && buy_signal != 0);
|
||||
bool has_sell = (sell_signal != EMPTY_VALUE && sell_signal != 0);
|
||||
|
||||
if(!has_buy && !has_sell)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[SignalDetector] No signal detected");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Validate: Both buy and sell signals should not be present
|
||||
if(has_buy && has_sell)
|
||||
{
|
||||
Print("[WARNING] Both BUY and SELL signals detected. Using BUY signal.");
|
||||
has_sell = false;
|
||||
}
|
||||
|
||||
result.has_signal = true;
|
||||
result.is_buy = has_buy;
|
||||
result.signal_price = has_buy ? buy_signal : sell_signal;
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[SignalDetector] ", (has_buy ? "BUY" : "SELL"), " signal detected at: ",
|
||||
DoubleToString(result.signal_price, m_digits));
|
||||
}
|
||||
|
||||
// Calculate SL/TP based on mode
|
||||
if(m_sltp_mode == 1)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[SignalDetector] Using Mode 1: Indicator SL/TP buffers");
|
||||
}
|
||||
|
||||
// Try indicator buffers first
|
||||
double sl_buffer = has_buy ?
|
||||
GetIndicatorBufferValue(m_buy_sl_buffer, bar_shift) :
|
||||
GetIndicatorBufferValue(m_sell_sl_buffer, bar_shift);
|
||||
|
||||
if(IsValidPrice(sl_buffer))
|
||||
{
|
||||
result.sl_price = sl_buffer;
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[SignalDetector] SL from indicator: ", DoubleToString(result.sl_price, m_digits));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[SignalDetector] SL buffer empty, will use ATR fallback");
|
||||
}
|
||||
}
|
||||
|
||||
// Read TP buffers
|
||||
int tp_count;
|
||||
if(has_buy)
|
||||
tp_count = ArraySize(m_buy_tp_buffers);
|
||||
else
|
||||
tp_count = ArraySize(m_sell_tp_buffers);
|
||||
|
||||
if(tp_count > 0)
|
||||
{
|
||||
ArrayResize(result.tp_prices, tp_count);
|
||||
result.tp_count = 0;
|
||||
|
||||
for(int i = 0; i < tp_count; i++)
|
||||
{
|
||||
int buffer_index;
|
||||
if(has_buy)
|
||||
buffer_index = m_buy_tp_buffers[i];
|
||||
else
|
||||
buffer_index = m_sell_tp_buffers[i];
|
||||
|
||||
double tp_buffer = GetIndicatorBufferValue(buffer_index, bar_shift);
|
||||
if(IsValidPrice(tp_buffer))
|
||||
{
|
||||
result.tp_prices[result.tp_count] = tp_buffer;
|
||||
result.tp_count++;
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[SignalDetector] TP", (i + 1), " from indicator:",
|
||||
DoubleToString(tp_buffer, m_digits));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[SignalDetector] Using Mode 0: ATR-based SL/TP");
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to ATR if SL/TP not set
|
||||
if(result.sl_price == 0 || result.tp_count == 0)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[SignalDetector] SL or TP not set from indicator, using ATR fallback");
|
||||
}
|
||||
|
||||
double atr = GetATRValue(bar_shift);
|
||||
if(atr > 0)
|
||||
{
|
||||
double open_price = SymbolInfoDouble(m_symbol, SYMBOL_BID);
|
||||
result.sl_price = CalculateATRSL(has_buy, atr, open_price);
|
||||
CalculateATRTPs(has_buy, atr, open_price, result.tp_prices, result.tp_count);
|
||||
result.used_atr_fallback = true;
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[SignalDetector] ATR value: ", DoubleToString(atr, m_digits));
|
||||
Print("[SignalDetector] SL from ATR: ", DoubleToString(result.sl_price, m_digits));
|
||||
for(int i = 0; i < result.tp_count; i++)
|
||||
{
|
||||
Print("[SignalDetector] TP", (i + 1), " from ATR: ",
|
||||
DoubleToString(result.tp_prices[i], m_digits));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("[ERROR] ATR value is 0, cannot calculate SL/TP");
|
||||
}
|
||||
}
|
||||
|
||||
// Apply minimum TP
|
||||
ApplyMinimumTP(has_buy, result.tp_prices, result.tp_count, result.signal_price);
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[SignalDetector] Signal detection complete. SL: ", DoubleToString(result.sl_price, m_digits),
|
||||
", TPs: ", result.tp_count, ", ATR fallback: ", (result.used_atr_fallback ? "Yes" : "No"));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get ATR value |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetATRValue(int bar_shift = 1)
|
||||
{
|
||||
if(m_atr_handle == INVALID_HANDLE)
|
||||
{
|
||||
Print("[ERROR] ATR handle is invalid");
|
||||
return 0;
|
||||
}
|
||||
|
||||
double atr[];
|
||||
ArraySetAsSeries(atr, true);
|
||||
|
||||
int copied = CopyBuffer(m_atr_handle, 0, bar_shift, 1, atr);
|
||||
if(copied <= 0)
|
||||
{
|
||||
int error = GetLastError();
|
||||
Print("[ERROR] Failed to copy ATR buffer. Error code: ", error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(atr[0] <= 0)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[SignalDetector] ATR value is 0 or negative: ", atr[0]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return atr[0];
|
||||
}
|
||||
|
||||
private:
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get indicator buffer value |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetIndicatorBufferValue(int buffer_index, int bar_shift)
|
||||
{
|
||||
if(m_indicator_handle == INVALID_HANDLE)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[SignalDetector] Indicator handle is invalid");
|
||||
}
|
||||
return EMPTY_VALUE;
|
||||
}
|
||||
|
||||
if(buffer_index < 0)
|
||||
{
|
||||
Print("[ERROR] Invalid buffer index: ", buffer_index);
|
||||
return EMPTY_VALUE;
|
||||
}
|
||||
|
||||
double buffer[];
|
||||
ArraySetAsSeries(buffer, true);
|
||||
|
||||
int copied = CopyBuffer(m_indicator_handle, buffer_index, bar_shift, 1, buffer);
|
||||
if(copied <= 0)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
int error = GetLastError();
|
||||
Print("[SignalDetector] Failed to copy buffer ", buffer_index,
|
||||
". Error code: ", error);
|
||||
}
|
||||
return EMPTY_VALUE;
|
||||
}
|
||||
|
||||
return buffer[0];
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate ATR-based SL |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateATRSL(bool is_buy, double atr_value, double open_price)
|
||||
{
|
||||
if(atr_value <= 0) return 0;
|
||||
|
||||
if(is_buy)
|
||||
{
|
||||
return NormalizeDouble(open_price - atr_value * m_sl_atr_multiplier, m_digits);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NormalizeDouble(open_price + atr_value * m_sl_atr_multiplier, m_digits);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate ATR-based TPs |
|
||||
//+------------------------------------------------------------------+
|
||||
void CalculateATRTPs(bool is_buy, double atr_value, double open_price, double &tp_prices[], int &tp_count)
|
||||
{
|
||||
if(atr_value <= 0) return;
|
||||
|
||||
// Default to 3 TPs if using ATR
|
||||
int num_tps = 3;
|
||||
ArrayResize(tp_prices, num_tps);
|
||||
tp_count = num_tps;
|
||||
|
||||
for(int i = 0; i < num_tps; i++)
|
||||
{
|
||||
double multiplier = m_tp_atr_multiplier * (i + 1);
|
||||
|
||||
if(is_buy)
|
||||
{
|
||||
tp_prices[i] = NormalizeDouble(open_price + atr_value * multiplier, m_digits);
|
||||
}
|
||||
else
|
||||
{
|
||||
tp_prices[i] = NormalizeDouble(open_price - atr_value * multiplier, m_digits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Apply minimum TP to all TPs |
|
||||
//+------------------------------------------------------------------+
|
||||
void ApplyMinimumTP(bool is_buy, double &tp_prices[], int tp_count, double open_price)
|
||||
{
|
||||
if(m_min_tp_pips <= 0)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[SignalDetector] Minimum TP disabled (0 pips)");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(tp_count == 0)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[SignalDetector] No TPs to apply minimum TP");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int adjusted_count = 0;
|
||||
|
||||
for(int i = 0; i < tp_count; i++)
|
||||
{
|
||||
if(is_buy)
|
||||
{
|
||||
double min_tp = NormalizeDouble(open_price + m_min_tp_pips * m_pip_value, m_digits);
|
||||
if(tp_prices[i] < min_tp)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[SignalDetector] TP", (i + 1), " adjusted to minimum: ",
|
||||
DoubleToString(tp_prices[i], m_digits), " -> ",
|
||||
DoubleToString(min_tp, m_digits));
|
||||
}
|
||||
tp_prices[i] = min_tp;
|
||||
adjusted_count++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
double min_tp = NormalizeDouble(open_price - m_min_tp_pips * m_pip_value, m_digits);
|
||||
if(tp_prices[i] > min_tp)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[SignalDetector] TP", (i + 1), " adjusted to minimum: ",
|
||||
DoubleToString(tp_prices[i], m_digits), " -> ",
|
||||
DoubleToString(min_tp, m_digits));
|
||||
}
|
||||
tp_prices[i] = min_tp;
|
||||
adjusted_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(m_enable_debug && adjusted_count > 0)
|
||||
{
|
||||
Print("[SignalDetector] Minimum TP applied to ", adjusted_count, " TP(s)");
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if price is valid |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsValidPrice(double price)
|
||||
{
|
||||
return (price != EMPTY_VALUE && price != 0 && price > 0);
|
||||
}
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
303
Buffer EA/Include/StateManager.mqh
Normal file
303
Buffer EA/Include/StateManager.mqh
Normal file
@@ -0,0 +1,303 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| StateManager.mqh |
|
||||
//| Universal Buffer Reader EA v2.0 |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025"
|
||||
#property link ""
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| CStateManager - Manages persistent state using Global Variables |
|
||||
//+------------------------------------------------------------------+
|
||||
class CStateManager
|
||||
{
|
||||
private:
|
||||
string m_symbol;
|
||||
int m_magic_number;
|
||||
string m_prefix;
|
||||
|
||||
string m_gv_accum_loss;
|
||||
string m_gv_consec_loss;
|
||||
|
||||
// Logging
|
||||
bool m_enable_debug;
|
||||
|
||||
public:
|
||||
//+------------------------------------------------------------------+
|
||||
//| Constructor |
|
||||
//+------------------------------------------------------------------+
|
||||
CStateManager()
|
||||
{
|
||||
m_symbol = "";
|
||||
m_magic_number = 0;
|
||||
m_prefix = "UnivBufEA";
|
||||
m_enable_debug = false;
|
||||
|
||||
m_gv_accum_loss = "";
|
||||
m_gv_consec_loss = "";
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Destructor |
|
||||
//+------------------------------------------------------------------+
|
||||
~CStateManager()
|
||||
{
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetParameters(string symbol, int magic_number, bool enable_debug = false)
|
||||
{
|
||||
// Validate parameters
|
||||
if(symbol == "")
|
||||
{
|
||||
Print("[ERROR] Invalid symbol: empty string");
|
||||
}
|
||||
m_symbol = symbol;
|
||||
|
||||
if(magic_number <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid magic number: ", magic_number, ". Using default 24680");
|
||||
m_magic_number = 24680;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_magic_number = magic_number;
|
||||
}
|
||||
|
||||
m_enable_debug = enable_debug;
|
||||
|
||||
// Build Global Variable names
|
||||
m_gv_accum_loss = BuildGVName("_AccumLoss");
|
||||
m_gv_consec_loss = BuildGVName("_ConsecLoss");
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[StateManager] Parameters set:");
|
||||
Print(" Symbol: ", m_symbol);
|
||||
Print(" Magic number: ", m_magic_number);
|
||||
Print(" GV Accum Loss: ", m_gv_accum_loss);
|
||||
Print(" GV Consec Loss: ", m_gv_consec_loss);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set debug mode |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetDebugMode(bool enable_debug)
|
||||
{
|
||||
m_enable_debug = enable_debug;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Load state from Global Variables |
|
||||
//+------------------------------------------------------------------+
|
||||
bool LoadState(double &accumulated_loss, int &consecutive_losses)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[StateManager] Loading state...");
|
||||
}
|
||||
|
||||
accumulated_loss = 0;
|
||||
consecutive_losses = 0;
|
||||
|
||||
// Load accumulated loss
|
||||
if(GlobalVariableCheck(m_gv_accum_loss))
|
||||
{
|
||||
accumulated_loss = GlobalVariableGet(m_gv_accum_loss);
|
||||
|
||||
// Validate accumulated loss
|
||||
if(accumulated_loss < 0)
|
||||
{
|
||||
Print("[WARNING] Invalid accumulated loss found: ", accumulated_loss, ". Resetting to 0");
|
||||
accumulated_loss = 0;
|
||||
GlobalVariableSet(m_gv_accum_loss, 0);
|
||||
}
|
||||
|
||||
Print("[StateManager] Loaded accumulated loss: ", DoubleToString(accumulated_loss, 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!GlobalVariableSet(m_gv_accum_loss, 0))
|
||||
{
|
||||
Print("[ERROR] Failed to initialize accumulated loss Global Variable");
|
||||
return false;
|
||||
}
|
||||
Print("[StateManager] Initialized accumulated loss: 0");
|
||||
}
|
||||
|
||||
// Load consecutive losses
|
||||
if(GlobalVariableCheck(m_gv_consec_loss))
|
||||
{
|
||||
consecutive_losses = (int)GlobalVariableGet(m_gv_consec_loss);
|
||||
|
||||
// Validate consecutive losses
|
||||
if(consecutive_losses < 0)
|
||||
{
|
||||
Print("[WARNING] Invalid consecutive losses found: ", consecutive_losses, ". Resetting to 0");
|
||||
consecutive_losses = 0;
|
||||
GlobalVariableSet(m_gv_consec_loss, 0);
|
||||
}
|
||||
|
||||
Print("[StateManager] Loaded consecutive losses: ", consecutive_losses);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!GlobalVariableSet(m_gv_consec_loss, 0))
|
||||
{
|
||||
Print("[ERROR] Failed to initialize consecutive losses Global Variable");
|
||||
return false;
|
||||
}
|
||||
Print("[StateManager] Initialized consecutive losses: 0");
|
||||
}
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[StateManager] State loaded successfully");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Save state to Global Variables |
|
||||
//+------------------------------------------------------------------+
|
||||
bool SaveState(double accumulated_loss, int consecutive_losses)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[StateManager] Saving state...");
|
||||
Print(" Accumulated loss: ", DoubleToString(accumulated_loss, 2));
|
||||
Print(" Consecutive losses: ", consecutive_losses);
|
||||
}
|
||||
|
||||
// Validate inputs
|
||||
if(accumulated_loss < 0)
|
||||
{
|
||||
Print("[ERROR] Invalid accumulated loss to save: ", accumulated_loss);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(consecutive_losses < 0)
|
||||
{
|
||||
Print("[ERROR] Invalid consecutive losses to save: ", consecutive_losses);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
|
||||
// Save accumulated loss
|
||||
if(!GlobalVariableSet(m_gv_accum_loss, accumulated_loss))
|
||||
{
|
||||
int error = GetLastError();
|
||||
Print("[ERROR] Failed to save accumulated loss. Error code: ", error);
|
||||
result = false;
|
||||
}
|
||||
|
||||
// Save consecutive losses
|
||||
if(!GlobalVariableSet(m_gv_consec_loss, consecutive_losses))
|
||||
{
|
||||
int error = GetLastError();
|
||||
Print("[ERROR] Failed to save consecutive losses. Error code: ", error);
|
||||
result = false;
|
||||
}
|
||||
|
||||
if(result && m_enable_debug)
|
||||
{
|
||||
Print("[StateManager] State saved successfully");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Clear state (for backtesting) |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ClearState()
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[StateManager] Clearing state...");
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
|
||||
// Delete accumulated loss
|
||||
if(GlobalVariableCheck(m_gv_accum_loss))
|
||||
{
|
||||
if(!GlobalVariableDel(m_gv_accum_loss))
|
||||
{
|
||||
int error = GetLastError();
|
||||
Print("[ERROR] Failed to delete accumulated loss. Error code: ", error);
|
||||
result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("[StateManager] Deleted accumulated loss Global Variable");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("[StateManager] Accumulated loss Global Variable not found (already cleared)");
|
||||
}
|
||||
|
||||
// Delete consecutive losses
|
||||
if(GlobalVariableCheck(m_gv_consec_loss))
|
||||
{
|
||||
if(!GlobalVariableDel(m_gv_consec_loss))
|
||||
{
|
||||
int error = GetLastError();
|
||||
Print("[ERROR] Failed to delete consecutive losses. Error code: ", error);
|
||||
result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("[StateManager] Deleted consecutive losses Global Variable");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("[StateManager] Consecutive losses Global Variable not found (already cleared)");
|
||||
}
|
||||
|
||||
if(result)
|
||||
{
|
||||
Print("[StateManager] State cleared successfully");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get Global Variable names |
|
||||
//+------------------------------------------------------------------+
|
||||
void GetGVNames(string &accum_loss_name, string &consec_loss_name)
|
||||
{
|
||||
accum_loss_name = m_gv_accum_loss;
|
||||
consec_loss_name = m_gv_consec_loss;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if state exists |
|
||||
//+------------------------------------------------------------------+
|
||||
bool StateExists()
|
||||
{
|
||||
bool accum_exists = GlobalVariableCheck(m_gv_accum_loss);
|
||||
bool consec_exists = GlobalVariableCheck(m_gv_consec_loss);
|
||||
|
||||
return (accum_exists || consec_exists);
|
||||
}
|
||||
|
||||
private:
|
||||
//+------------------------------------------------------------------+
|
||||
//| Build Global Variable name |
|
||||
//+------------------------------------------------------------------+
|
||||
string BuildGVName(string suffix)
|
||||
{
|
||||
return m_prefix + "_" + m_symbol + "_" + IntegerToString(m_magic_number) + suffix;
|
||||
}
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
250
Buffer EA/Include/TimeFilter.mqh
Normal file
250
Buffer EA/Include/TimeFilter.mqh
Normal file
@@ -0,0 +1,250 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| TimeFilter.mqh |
|
||||
//| Universal Buffer Reader EA v2.0 |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025"
|
||||
#property link ""
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| CTimeFilter - Validates day of week and trading session filters |
|
||||
//+------------------------------------------------------------------+
|
||||
class CTimeFilter
|
||||
{
|
||||
private:
|
||||
bool m_enabled;
|
||||
bool m_days_enabled[7]; // 0=Sunday, 1=Monday, ..., 6=Saturday
|
||||
bool m_asian_enabled;
|
||||
bool m_europe_enabled;
|
||||
bool m_america_enabled;
|
||||
|
||||
// Logging
|
||||
bool m_enable_debug;
|
||||
|
||||
public:
|
||||
//+------------------------------------------------------------------+
|
||||
//| Constructor |
|
||||
//+------------------------------------------------------------------+
|
||||
CTimeFilter()
|
||||
{
|
||||
m_enabled = false;
|
||||
m_enable_debug = false;
|
||||
|
||||
// Default: Mon-Fri enabled
|
||||
for(int i = 0; i < 7; i++)
|
||||
{
|
||||
m_days_enabled[i] = (i >= 1 && i <= 5);
|
||||
}
|
||||
|
||||
m_asian_enabled = true;
|
||||
m_europe_enabled = true;
|
||||
m_america_enabled = true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Destructor |
|
||||
//+------------------------------------------------------------------+
|
||||
~CTimeFilter()
|
||||
{
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetParameters(
|
||||
bool enabled,
|
||||
bool mon, bool tue, bool wed, bool thu, bool fri, bool sat, bool sun,
|
||||
bool asian, bool europe, bool america,
|
||||
bool enable_debug = false
|
||||
)
|
||||
{
|
||||
m_enabled = enabled;
|
||||
m_days_enabled[1] = mon; // Monday
|
||||
m_days_enabled[2] = tue; // Tuesday
|
||||
m_days_enabled[3] = wed; // Wednesday
|
||||
m_days_enabled[4] = thu; // Thursday
|
||||
m_days_enabled[5] = fri; // Friday
|
||||
m_days_enabled[6] = sat; // Saturday
|
||||
m_days_enabled[0] = sun; // Sunday
|
||||
|
||||
m_asian_enabled = asian;
|
||||
m_europe_enabled = europe;
|
||||
m_america_enabled = america;
|
||||
m_enable_debug = enable_debug;
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[TimeFilter] Parameters set - Enabled: ", m_enabled);
|
||||
Print("[TimeFilter] Days: Mon=", mon, " Tue=", tue, " Wed=", wed,
|
||||
" Thu=", thu, " Fri=", fri, " Sat=", sat, " Sun=", sun);
|
||||
Print("[TimeFilter] Sessions: Asian=", asian, " Europe=", europe, " America=", america);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set debug mode |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetDebugMode(bool enable_debug)
|
||||
{
|
||||
m_enable_debug = enable_debug;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if current time is allowed for trading |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsTimeAllowed()
|
||||
{
|
||||
if(!m_enabled)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[TimeFilter] Time filtering disabled - Trading allowed");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool day_allowed = IsDayOfWeekAllowed();
|
||||
bool session_allowed = IsSessionTimeAllowed();
|
||||
|
||||
bool result = (day_allowed && session_allowed);
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[TimeFilter] Time check - Day allowed: ", day_allowed,
|
||||
", Session allowed: ", session_allowed,
|
||||
", Result: ", (result ? "ALLOWED" : "BLOCKED"));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get current day of week name |
|
||||
//+------------------------------------------------------------------+
|
||||
string GetCurrentDayOfWeek()
|
||||
{
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(TimeGMT(), dt);
|
||||
|
||||
string day_names[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
|
||||
return day_names[dt.day_of_week];
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get current session name |
|
||||
//+------------------------------------------------------------------+
|
||||
string GetCurrentSession()
|
||||
{
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(TimeGMT(), dt);
|
||||
int hour = dt.hour;
|
||||
|
||||
if(IsAsianSession(hour)) return "Asian";
|
||||
if(IsEuropeSession(hour)) return "Europe";
|
||||
if(IsAmericaSession(hour)) return "America";
|
||||
|
||||
return "Off-hours";
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get current GMT time as string |
|
||||
//+------------------------------------------------------------------+
|
||||
string GetCurrentGMTTime()
|
||||
{
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(TimeGMT(), dt);
|
||||
|
||||
return StringFormat("%02d:%02d:%02d GMT", dt.hour, dt.min, dt.sec);
|
||||
}
|
||||
|
||||
private:
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if current day of week is allowed |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsDayOfWeekAllowed()
|
||||
{
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(TimeGMT(), dt);
|
||||
|
||||
string day_names[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
|
||||
bool allowed = m_days_enabled[dt.day_of_week];
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[TimeFilter] Day of week: ", day_names[dt.day_of_week],
|
||||
" (", dt.day_of_week, ") - ", (allowed ? "ALLOWED" : "BLOCKED"));
|
||||
}
|
||||
|
||||
return allowed;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if current session is allowed |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsSessionTimeAllowed()
|
||||
{
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(TimeGMT(), dt);
|
||||
int hour = dt.hour;
|
||||
|
||||
bool in_asian = IsAsianSession(hour);
|
||||
bool in_europe = IsEuropeSession(hour);
|
||||
bool in_america = IsAmericaSession(hour);
|
||||
|
||||
bool allowed = false;
|
||||
string active_session = "None";
|
||||
|
||||
if(in_asian && m_asian_enabled)
|
||||
{
|
||||
allowed = true;
|
||||
active_session = "Asian";
|
||||
}
|
||||
else if(in_europe && m_europe_enabled)
|
||||
{
|
||||
allowed = true;
|
||||
active_session = "Europe";
|
||||
}
|
||||
else if(in_america && m_america_enabled)
|
||||
{
|
||||
allowed = true;
|
||||
active_session = "America";
|
||||
}
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[TimeFilter] Current time: ", dt.hour, ":00 GMT");
|
||||
Print("[TimeFilter] In Asian session (00-08): ", in_asian, " - Enabled: ", m_asian_enabled);
|
||||
Print("[TimeFilter] In Europe session (07-16): ", in_europe, " - Enabled: ", m_europe_enabled);
|
||||
Print("[TimeFilter] In America session (13-22): ", in_america, " - Enabled: ", m_america_enabled);
|
||||
Print("[TimeFilter] Active session: ", active_session, " - ", (allowed ? "ALLOWED" : "BLOCKED"));
|
||||
}
|
||||
|
||||
return allowed;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if Asian session (00:00 - 08:00 GMT) |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsAsianSession(int hour)
|
||||
{
|
||||
return (hour >= 0 && hour < 8);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if Europe session (07:00 - 16:00 GMT) |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsEuropeSession(int hour)
|
||||
{
|
||||
return (hour >= 7 && hour < 16);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if America session (13:00 - 22:00 GMT) |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsAmericaSession(int hour)
|
||||
{
|
||||
return (hour >= 13 && hour < 22);
|
||||
}
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
494
Buffer EA/Include/TradeExecutor.mqh
Normal file
494
Buffer EA/Include/TradeExecutor.mqh
Normal file
@@ -0,0 +1,494 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| TradeExecutor.mqh |
|
||||
//| Universal Buffer Reader EA v2.0 |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025"
|
||||
#property link ""
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
#include <Trade\PositionInfo.mqh>
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| CTradeExecutor - Executes trades and manages orders |
|
||||
//+------------------------------------------------------------------+
|
||||
class CTradeExecutor
|
||||
{
|
||||
private:
|
||||
int m_slippage_points;
|
||||
int m_magic_number;
|
||||
string m_symbol;
|
||||
int m_digits;
|
||||
bool m_take_screenshot;
|
||||
long m_chart_id;
|
||||
string m_indicator_name;
|
||||
|
||||
CTrade *m_trade;
|
||||
CPositionInfo *m_position_info;
|
||||
|
||||
// Logging
|
||||
bool m_enable_debug;
|
||||
|
||||
public:
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trade result structure |
|
||||
//+------------------------------------------------------------------+
|
||||
struct TradeResult
|
||||
{
|
||||
bool success;
|
||||
ulong ticket;
|
||||
string error_message;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Constructor |
|
||||
//+------------------------------------------------------------------+
|
||||
CTradeExecutor()
|
||||
{
|
||||
m_slippage_points = 30;
|
||||
m_magic_number = 24680;
|
||||
m_symbol = "";
|
||||
m_digits = 0;
|
||||
m_take_screenshot = true;
|
||||
m_chart_id = 0;
|
||||
m_indicator_name = "";
|
||||
m_enable_debug = false;
|
||||
|
||||
m_trade = new CTrade();
|
||||
m_position_info = new CPositionInfo();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Destructor |
|
||||
//+------------------------------------------------------------------+
|
||||
~CTradeExecutor()
|
||||
{
|
||||
if(m_trade != NULL)
|
||||
{
|
||||
delete m_trade;
|
||||
m_trade = NULL;
|
||||
}
|
||||
|
||||
if(m_position_info != NULL)
|
||||
{
|
||||
delete m_position_info;
|
||||
m_position_info = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetParameters(
|
||||
int slippage_points,
|
||||
int magic_number,
|
||||
string symbol,
|
||||
int digits,
|
||||
bool take_screenshot,
|
||||
long chart_id,
|
||||
string indicator_name,
|
||||
bool enable_debug = false
|
||||
)
|
||||
{
|
||||
// Validate parameters
|
||||
if(slippage_points < 0)
|
||||
{
|
||||
Print("[ERROR] Invalid slippage points: ", slippage_points, ". Using default 30");
|
||||
m_slippage_points = 30;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_slippage_points = slippage_points;
|
||||
}
|
||||
|
||||
if(magic_number <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid magic number: ", magic_number, ". Using default 24680");
|
||||
m_magic_number = 24680;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_magic_number = magic_number;
|
||||
}
|
||||
|
||||
if(symbol == "")
|
||||
{
|
||||
Print("[ERROR] Invalid symbol: empty string");
|
||||
}
|
||||
m_symbol = symbol;
|
||||
|
||||
if(digits <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid digits: ", digits);
|
||||
}
|
||||
m_digits = digits;
|
||||
|
||||
m_take_screenshot = take_screenshot;
|
||||
m_chart_id = chart_id;
|
||||
m_indicator_name = indicator_name;
|
||||
m_enable_debug = enable_debug;
|
||||
|
||||
m_trade.SetExpertMagicNumber(m_magic_number);
|
||||
m_trade.SetDeviationInPoints(m_slippage_points);
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[TradeExecutor] Parameters set:");
|
||||
Print(" Slippage: ", m_slippage_points, " points");
|
||||
Print(" Magic number: ", m_magic_number);
|
||||
Print(" Symbol: ", m_symbol);
|
||||
Print(" Digits: ", m_digits);
|
||||
Print(" Take screenshot: ", m_take_screenshot);
|
||||
Print(" Chart ID: ", m_chart_id);
|
||||
Print(" Indicator: ", m_indicator_name);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set debug mode |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetDebugMode(bool enable_debug)
|
||||
{
|
||||
m_enable_debug = enable_debug;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open new trade with multiple TPs |
|
||||
//+------------------------------------------------------------------+
|
||||
TradeResult ExecuteTrade(
|
||||
bool is_buy,
|
||||
double lots,
|
||||
double open_price,
|
||||
double sl_price,
|
||||
double &tp_prices[],
|
||||
int tp_count
|
||||
)
|
||||
{
|
||||
TradeResult result;
|
||||
result.success = false;
|
||||
result.ticket = 0;
|
||||
result.error_message = "";
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[TradeExecutor] Executing trade...");
|
||||
Print(" Direction: ", (is_buy ? "BUY" : "SELL"));
|
||||
Print(" Lots: ", lots);
|
||||
Print(" SL: ", sl_price);
|
||||
Print(" TP count: ", tp_count);
|
||||
}
|
||||
|
||||
// Validate inputs
|
||||
if(lots <= 0)
|
||||
{
|
||||
result.error_message = "Invalid lots: " + DoubleToString(lots, 2);
|
||||
Print("[ERROR] ", result.error_message);
|
||||
return result;
|
||||
}
|
||||
|
||||
if(m_symbol == "")
|
||||
{
|
||||
result.error_message = "Symbol not set";
|
||||
Print("[ERROR] ", result.error_message);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Build order comment with multiple TPs
|
||||
string comment = BuildOrderComment(tp_prices, tp_count);
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print(" Comment: ", comment);
|
||||
}
|
||||
|
||||
// Get execution price
|
||||
double execution_price = GetExecutionPrice(is_buy);
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print(" Execution price: ", execution_price);
|
||||
}
|
||||
|
||||
// Execute order
|
||||
bool order_result = false;
|
||||
if(is_buy)
|
||||
{
|
||||
order_result = m_trade.Buy(lots, m_symbol, execution_price, sl_price, 0, comment);
|
||||
}
|
||||
else
|
||||
{
|
||||
order_result = m_trade.Sell(lots, m_symbol, execution_price, sl_price, 0, comment);
|
||||
}
|
||||
|
||||
if(order_result)
|
||||
{
|
||||
result.success = true;
|
||||
result.ticket = m_trade.ResultOrder();
|
||||
|
||||
Print("[TradeExecutor] Order opened successfully. Ticket: ", result.ticket);
|
||||
|
||||
// Take screenshot
|
||||
if(m_take_screenshot)
|
||||
{
|
||||
TakeScreenshot(result.ticket);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int error_code = m_trade.ResultRetcode();
|
||||
result.error_message = "OrderSend failed: " + m_trade.ResultRetcodeDescription() +
|
||||
" (Code: " + IntegerToString(error_code) + ")";
|
||||
Print("[ERROR] ", result.error_message);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Partial close |
|
||||
//+------------------------------------------------------------------+
|
||||
TradeResult ExecutePartialClose(
|
||||
ulong ticket,
|
||||
double close_lots,
|
||||
int tp_index
|
||||
)
|
||||
{
|
||||
TradeResult result;
|
||||
result.success = false;
|
||||
result.ticket = 0;
|
||||
result.error_message = "";
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[TradeExecutor] Executing partial close...");
|
||||
Print(" Ticket: ", ticket);
|
||||
Print(" Close lots: ", close_lots);
|
||||
Print(" TP index: ", tp_index);
|
||||
}
|
||||
|
||||
// Validate inputs
|
||||
if(ticket == 0)
|
||||
{
|
||||
result.error_message = "Invalid ticket: 0";
|
||||
Print("[ERROR] ", result.error_message);
|
||||
return result;
|
||||
}
|
||||
|
||||
if(close_lots <= 0)
|
||||
{
|
||||
result.error_message = "Invalid close lots: " + DoubleToString(close_lots, 2);
|
||||
Print("[ERROR] ", result.error_message);
|
||||
return result;
|
||||
}
|
||||
|
||||
if(!PositionSelectByTicket(ticket))
|
||||
{
|
||||
result.error_message = "Failed to select position #" + IntegerToString(ticket);
|
||||
Print("[ERROR] ", result.error_message);
|
||||
return result;
|
||||
}
|
||||
|
||||
double current_lots = PositionGetDouble(POSITION_VOLUME);
|
||||
if(close_lots > current_lots)
|
||||
{
|
||||
Print("[WARNING] Close lots (", close_lots, ") > current lots (", current_lots,
|
||||
") for ticket #", ticket, ". Adjusting to current lots.");
|
||||
close_lots = current_lots;
|
||||
}
|
||||
|
||||
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
bool close_result = false;
|
||||
if(pos_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
close_result = m_trade.Sell(close_lots, m_symbol, 0, 0, 0, "Partial Close TP" + IntegerToString(tp_index));
|
||||
}
|
||||
else
|
||||
{
|
||||
close_result = m_trade.Buy(close_lots, m_symbol, 0, 0, 0, "Partial Close TP" + IntegerToString(tp_index));
|
||||
}
|
||||
|
||||
if(close_result)
|
||||
{
|
||||
result.success = true;
|
||||
result.ticket = m_trade.ResultOrder();
|
||||
Print("[TradeExecutor] Partial close executed: ", close_lots, " lots at TP", tp_index,
|
||||
" for ticket #", ticket);
|
||||
}
|
||||
else
|
||||
{
|
||||
int error_code = m_trade.ResultRetcode();
|
||||
result.error_message = "Partial close failed: " + m_trade.ResultRetcodeDescription() +
|
||||
" (Code: " + IntegerToString(error_code) + ")";
|
||||
Print("[ERROR] ", result.error_message);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if trade is open |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsTradeOpen()
|
||||
{
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(m_position_info.SelectByIndex(i))
|
||||
{
|
||||
if(m_position_info.Symbol() == m_symbol &&
|
||||
m_position_info.Magic() == m_magic_number)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close opposite trade |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CloseOppositeTrade(bool is_buy)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[TradeExecutor] Closing opposite trades for ", (is_buy ? "BUY" : "SELL"), " signal");
|
||||
}
|
||||
|
||||
bool closed_any = false;
|
||||
|
||||
ENUM_POSITION_TYPE opposite_type = is_buy ? POSITION_TYPE_SELL : POSITION_TYPE_BUY;
|
||||
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(m_position_info.SelectByIndex(i))
|
||||
{
|
||||
if(m_position_info.Symbol() == m_symbol &&
|
||||
m_position_info.Magic() == m_magic_number &&
|
||||
m_position_info.PositionType() == opposite_type)
|
||||
{
|
||||
ulong ticket = m_position_info.Ticket();
|
||||
double lots = m_position_info.Volume();
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[TradeExecutor] Found opposite trade #", ticket, " (", lots, " lots)");
|
||||
}
|
||||
|
||||
if(m_trade.PositionClose(ticket))
|
||||
{
|
||||
Print("[TradeExecutor] Closed opposite trade Ticket#", ticket, " due to new signal.");
|
||||
closed_any = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int error_code = m_trade.ResultRetcode();
|
||||
Print("[ERROR] Failed to close opposite trade Ticket#", ticket,
|
||||
". Error: ", m_trade.ResultRetcodeDescription(), " (", error_code, ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(m_enable_debug && !closed_any)
|
||||
{
|
||||
Print("[TradeExecutor] No opposite trades found to close");
|
||||
}
|
||||
|
||||
return closed_any;
|
||||
}
|
||||
|
||||
private:
|
||||
//+------------------------------------------------------------------+
|
||||
//| Build order comment with multiple TPs |
|
||||
//+------------------------------------------------------------------+
|
||||
string BuildOrderComment(double &tp_prices[], int tp_count)
|
||||
{
|
||||
string comment = "UnivBufEA_" + IntegerToString(m_magic_number);
|
||||
|
||||
// Add TPs to comment
|
||||
for(int i = 0; i < tp_count; i++)
|
||||
{
|
||||
comment += ";TP" + IntegerToString(i + 1) + "=" + DoubleToString(tp_prices[i], m_digits);
|
||||
}
|
||||
|
||||
// Add breakeven and trailing flags
|
||||
comment += ";BE=0;TS=0";
|
||||
|
||||
// Truncate to max length (31 chars for some brokers)
|
||||
if(StringLen(comment) > 31)
|
||||
{
|
||||
comment = StringSubstr(comment, 0, 31);
|
||||
}
|
||||
|
||||
return comment;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Take screenshot |
|
||||
//+------------------------------------------------------------------+
|
||||
bool TakeScreenshot(ulong ticket)
|
||||
{
|
||||
if(!m_take_screenshot)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[TradeExecutor] Screenshot disabled");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if(m_chart_id == 0)
|
||||
{
|
||||
Print("[ERROR] Chart ID is 0, cannot take screenshot");
|
||||
return false;
|
||||
}
|
||||
|
||||
string time_str = TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES|TIME_SECONDS);
|
||||
StringReplace(time_str, ":", "_");
|
||||
StringReplace(time_str, ".", "_");
|
||||
|
||||
string filename = m_symbol + "_T" + IntegerToString(ticket) + "_" + time_str + ".gif";
|
||||
|
||||
int width = (int)ChartGetInteger(m_chart_id, CHART_WIDTH_IN_PIXELS);
|
||||
int height = (int)ChartGetInteger(m_chart_id, CHART_HEIGHT_IN_PIXELS);
|
||||
|
||||
if(width <= 0 || height <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid chart dimensions: ", width, "x", height);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[TradeExecutor] Taking screenshot: ", filename, " (", width, "x", height, ")");
|
||||
}
|
||||
|
||||
if(ChartScreenShot(m_chart_id, filename, width, height, ALIGN_RIGHT))
|
||||
{
|
||||
Print("[TradeExecutor] Screenshot saved: ", filename);
|
||||
return true;
|
||||
}
|
||||
|
||||
int error = GetLastError();
|
||||
Print("[ERROR] Failed to save screenshot. Error code: ", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get execution price |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetExecutionPrice(bool is_buy)
|
||||
{
|
||||
if(is_buy)
|
||||
{
|
||||
return SymbolInfoDouble(m_symbol, SYMBOL_ASK);
|
||||
}
|
||||
else
|
||||
{
|
||||
return SymbolInfoDouble(m_symbol, SYMBOL_BID);
|
||||
}
|
||||
}
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
430
Buffer EA/Include/UIManager.mqh
Normal file
430
Buffer EA/Include/UIManager.mqh
Normal file
@@ -0,0 +1,430 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| UIManager.mqh |
|
||||
//| Universal Buffer Reader EA v2.0 |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025"
|
||||
#property link ""
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| CUIManager - Manages chart labels and manual mode UI |
|
||||
//+------------------------------------------------------------------+
|
||||
class CUIManager
|
||||
{
|
||||
private:
|
||||
long m_chart_id;
|
||||
bool m_manual_mode;
|
||||
int m_magic_number;
|
||||
string m_symbol;
|
||||
double m_accumulated_loss;
|
||||
double m_loss_percent;
|
||||
double m_high_loss_threshold;
|
||||
|
||||
// Logging
|
||||
bool m_enable_debug;
|
||||
|
||||
public:
|
||||
//+------------------------------------------------------------------+
|
||||
//| Constructor |
|
||||
//+------------------------------------------------------------------+
|
||||
CUIManager()
|
||||
{
|
||||
m_chart_id = 0;
|
||||
m_manual_mode = false;
|
||||
m_magic_number = 0;
|
||||
m_symbol = "";
|
||||
m_accumulated_loss = 0;
|
||||
m_loss_percent = 0;
|
||||
m_high_loss_threshold = 0;
|
||||
m_enable_debug = false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Destructor |
|
||||
//+------------------------------------------------------------------+
|
||||
~CUIManager()
|
||||
{
|
||||
DeleteUI();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetParameters(
|
||||
long chart_id,
|
||||
bool manual_mode,
|
||||
int magic_number,
|
||||
string symbol,
|
||||
double high_loss_threshold,
|
||||
bool enable_debug = false
|
||||
)
|
||||
{
|
||||
// Validate parameters
|
||||
if(chart_id == 0)
|
||||
{
|
||||
Print("[ERROR] Invalid chart ID: 0");
|
||||
}
|
||||
m_chart_id = chart_id;
|
||||
|
||||
if(magic_number <= 0)
|
||||
{
|
||||
Print("[ERROR] Invalid magic number: ", magic_number, ". Using default 24680");
|
||||
m_magic_number = 24680;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_magic_number = magic_number;
|
||||
}
|
||||
|
||||
if(symbol == "")
|
||||
{
|
||||
Print("[ERROR] Invalid symbol: empty string");
|
||||
}
|
||||
m_symbol = symbol;
|
||||
|
||||
if(high_loss_threshold < 0)
|
||||
{
|
||||
Print("[ERROR] Invalid high loss threshold: ", high_loss_threshold, ". Using 0");
|
||||
m_high_loss_threshold = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_high_loss_threshold = high_loss_threshold;
|
||||
}
|
||||
|
||||
m_manual_mode = manual_mode;
|
||||
m_enable_debug = enable_debug;
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[UIManager] Parameters set:");
|
||||
Print(" Chart ID: ", m_chart_id);
|
||||
Print(" Manual mode: ", m_manual_mode);
|
||||
Print(" Magic number: ", m_magic_number);
|
||||
Print(" Symbol: ", m_symbol);
|
||||
Print(" High loss threshold: ", m_high_loss_threshold, "%");
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set debug mode |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetDebugMode(bool enable_debug)
|
||||
{
|
||||
m_enable_debug = enable_debug;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Create UI elements |
|
||||
//+------------------------------------------------------------------+
|
||||
void CreateUI()
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[UIManager] Creating UI elements...");
|
||||
}
|
||||
|
||||
if(m_chart_id == 0)
|
||||
{
|
||||
Print("[ERROR] Cannot create UI: Chart ID is 0");
|
||||
return;
|
||||
}
|
||||
|
||||
CreateLossLabels();
|
||||
|
||||
if(m_manual_mode)
|
||||
{
|
||||
CreateManualModeControls();
|
||||
}
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[UIManager] UI elements created successfully");
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Delete UI elements |
|
||||
//+------------------------------------------------------------------+
|
||||
void DeleteUI()
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[UIManager] Deleting UI elements...");
|
||||
}
|
||||
|
||||
int deleted_count = 0;
|
||||
|
||||
if(ObjectDelete(m_chart_id, "AccumLossLabel")) deleted_count++;
|
||||
if(ObjectDelete(m_chart_id, "AccumLossPercentLabel")) deleted_count++;
|
||||
|
||||
if(m_manual_mode)
|
||||
{
|
||||
if(ObjectDelete(m_chart_id, "ManualTPLabel")) deleted_count++;
|
||||
if(ObjectDelete(m_chart_id, "ManualTPEdit")) deleted_count++;
|
||||
if(ObjectDelete(m_chart_id, "ManualSLLabel")) deleted_count++;
|
||||
if(ObjectDelete(m_chart_id, "ManualSLEdit")) deleted_count++;
|
||||
if(ObjectDelete(m_chart_id, "ManualTradeButton")) deleted_count++;
|
||||
}
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[UIManager] Deleted ", deleted_count, " UI elements");
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update loss display |
|
||||
//+------------------------------------------------------------------+
|
||||
void UpdateLossDisplay(double accumulated_loss, double loss_percent)
|
||||
{
|
||||
m_accumulated_loss = accumulated_loss;
|
||||
m_loss_percent = loss_percent;
|
||||
|
||||
// Validate inputs
|
||||
if(accumulated_loss < 0)
|
||||
{
|
||||
Print("[WARNING] Invalid accumulated loss: ", accumulated_loss, ". Using 0");
|
||||
accumulated_loss = 0;
|
||||
}
|
||||
|
||||
if(loss_percent < 0)
|
||||
{
|
||||
Print("[WARNING] Invalid loss percent: ", loss_percent, "%. Using 0%");
|
||||
loss_percent = 0;
|
||||
}
|
||||
|
||||
string loss_text = "Accum. Loss: " + DoubleToString(accumulated_loss, 2);
|
||||
string percent_text = "Loss % of Bal: " + DoubleToString(loss_percent, 2) + "%";
|
||||
|
||||
color label_color = clrWhite;
|
||||
bool is_high_loss = false;
|
||||
|
||||
if(m_high_loss_threshold > 0 && loss_percent >= m_high_loss_threshold)
|
||||
{
|
||||
label_color = clrOrangeRed;
|
||||
is_high_loss = true;
|
||||
}
|
||||
|
||||
// Update labels
|
||||
ObjectSetString(m_chart_id, "AccumLossLabel", OBJPROP_TEXT, loss_text);
|
||||
ObjectSetInteger(m_chart_id, "AccumLossLabel", OBJPROP_COLOR, label_color);
|
||||
|
||||
ObjectSetString(m_chart_id, "AccumLossPercentLabel", OBJPROP_TEXT, percent_text);
|
||||
ObjectSetInteger(m_chart_id, "AccumLossPercentLabel", OBJPROP_COLOR, label_color);
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[UIManager] Loss display updated: ", loss_text, ", ", percent_text,
|
||||
(is_high_loss ? " [HIGH LOSS WARNING]" : ""));
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update manual mode UI |
|
||||
//+------------------------------------------------------------------+
|
||||
void UpdateManualModeUI()
|
||||
{
|
||||
// Update any dynamic manual mode elements if needed
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get manual TP from UI |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetManualTP()
|
||||
{
|
||||
if(!m_manual_mode)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[UIManager] Manual mode not enabled, returning TP = 0");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
string tp_str = ObjectGetString(m_chart_id, "ManualTPEdit", OBJPROP_TEXT);
|
||||
double tp_value = StringToDouble(tp_str);
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[UIManager] Manual TP: ", tp_value);
|
||||
}
|
||||
|
||||
return tp_value;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get manual SL from UI |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetManualSL()
|
||||
{
|
||||
if(!m_manual_mode)
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[UIManager] Manual mode not enabled, returning SL = 0");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
string sl_str = ObjectGetString(m_chart_id, "ManualSLEdit", OBJPROP_TEXT);
|
||||
double sl_value = StringToDouble(sl_str);
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[UIManager] Manual SL: ", sl_value);
|
||||
}
|
||||
|
||||
return sl_value;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if manual trade button was clicked |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsManualTradeButtonClicked()
|
||||
{
|
||||
if(!m_manual_mode) return false;
|
||||
|
||||
// This will be checked in OnChartEvent
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
//+------------------------------------------------------------------+
|
||||
//| Create loss display labels |
|
||||
//+------------------------------------------------------------------+
|
||||
void CreateLossLabels()
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[UIManager] Creating loss display labels...");
|
||||
}
|
||||
|
||||
// Accumulated Loss Label
|
||||
if(!ObjectCreate(m_chart_id, "AccumLossLabel", OBJ_LABEL, 0, 0, 0))
|
||||
{
|
||||
Print("[ERROR] Failed to create AccumLossLabel. Error: ", GetLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
ObjectSetString(m_chart_id, "AccumLossLabel", OBJPROP_TEXT, "Accum. Loss: Loading...");
|
||||
ObjectSetInteger(m_chart_id, "AccumLossLabel", OBJPROP_CORNER, CORNER_LEFT_LOWER);
|
||||
ObjectSetInteger(m_chart_id, "AccumLossLabel", OBJPROP_XDISTANCE, 10);
|
||||
ObjectSetInteger(m_chart_id, "AccumLossLabel", OBJPROP_YDISTANCE, 40);
|
||||
ObjectSetInteger(m_chart_id, "AccumLossLabel", OBJPROP_FONTSIZE, 10);
|
||||
ObjectSetInteger(m_chart_id, "AccumLossLabel", OBJPROP_COLOR, clrWhite);
|
||||
}
|
||||
|
||||
// Loss Percent Label
|
||||
if(!ObjectCreate(m_chart_id, "AccumLossPercentLabel", OBJ_LABEL, 0, 0, 0))
|
||||
{
|
||||
Print("[ERROR] Failed to create AccumLossPercentLabel. Error: ", GetLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
ObjectSetString(m_chart_id, "AccumLossPercentLabel", OBJPROP_TEXT, "Loss % of Bal: Loading...");
|
||||
ObjectSetInteger(m_chart_id, "AccumLossPercentLabel", OBJPROP_CORNER, CORNER_LEFT_LOWER);
|
||||
ObjectSetInteger(m_chart_id, "AccumLossPercentLabel", OBJPROP_XDISTANCE, 10);
|
||||
ObjectSetInteger(m_chart_id, "AccumLossPercentLabel", OBJPROP_YDISTANCE, 20);
|
||||
ObjectSetInteger(m_chart_id, "AccumLossPercentLabel", OBJPROP_FONTSIZE, 10);
|
||||
ObjectSetInteger(m_chart_id, "AccumLossPercentLabel", OBJPROP_COLOR, clrWhite);
|
||||
}
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[UIManager] Loss display labels created");
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Create manual mode controls |
|
||||
//+------------------------------------------------------------------+
|
||||
void CreateManualModeControls()
|
||||
{
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[UIManager] Creating manual mode controls...");
|
||||
}
|
||||
|
||||
int created_count = 0;
|
||||
|
||||
// Manual TP Label
|
||||
if(ObjectCreate(m_chart_id, "ManualTPLabel", OBJ_LABEL, 0, 0, 0))
|
||||
{
|
||||
ObjectSetString(m_chart_id, "ManualTPLabel", OBJPROP_TEXT, "Take Profit:");
|
||||
ObjectSetInteger(m_chart_id, "ManualTPLabel", OBJPROP_CORNER, CORNER_RIGHT_UPPER);
|
||||
ObjectSetInteger(m_chart_id, "ManualTPLabel", OBJPROP_XDISTANCE, 180);
|
||||
ObjectSetInteger(m_chart_id, "ManualTPLabel", OBJPROP_YDISTANCE, 50);
|
||||
created_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("[ERROR] Failed to create ManualTPLabel. Error: ", GetLastError());
|
||||
}
|
||||
|
||||
// Manual TP Edit
|
||||
if(ObjectCreate(m_chart_id, "ManualTPEdit", OBJ_EDIT, 0, 0, 0))
|
||||
{
|
||||
ObjectSetInteger(m_chart_id, "ManualTPEdit", OBJPROP_CORNER, CORNER_RIGHT_UPPER);
|
||||
ObjectSetInteger(m_chart_id, "ManualTPEdit", OBJPROP_XDISTANCE, 110);
|
||||
ObjectSetInteger(m_chart_id, "ManualTPEdit", OBJPROP_YDISTANCE, 50);
|
||||
ObjectSetInteger(m_chart_id, "ManualTPEdit", OBJPROP_XSIZE, 60);
|
||||
created_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("[ERROR] Failed to create ManualTPEdit. Error: ", GetLastError());
|
||||
}
|
||||
|
||||
// Manual SL Label
|
||||
if(ObjectCreate(m_chart_id, "ManualSLLabel", OBJ_LABEL, 0, 0, 0))
|
||||
{
|
||||
ObjectSetString(m_chart_id, "ManualSLLabel", OBJPROP_TEXT, "Stop Loss:");
|
||||
ObjectSetInteger(m_chart_id, "ManualSLLabel", OBJPROP_CORNER, CORNER_RIGHT_UPPER);
|
||||
ObjectSetInteger(m_chart_id, "ManualSLLabel", OBJPROP_XDISTANCE, 180);
|
||||
ObjectSetInteger(m_chart_id, "ManualSLLabel", OBJPROP_YDISTANCE, 70);
|
||||
created_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("[ERROR] Failed to create ManualSLLabel. Error: ", GetLastError());
|
||||
}
|
||||
|
||||
// Manual SL Edit
|
||||
if(ObjectCreate(m_chart_id, "ManualSLEdit", OBJ_EDIT, 0, 0, 0))
|
||||
{
|
||||
ObjectSetInteger(m_chart_id, "ManualSLEdit", OBJPROP_CORNER, CORNER_RIGHT_UPPER);
|
||||
ObjectSetInteger(m_chart_id, "ManualSLEdit", OBJPROP_XDISTANCE, 110);
|
||||
ObjectSetInteger(m_chart_id, "ManualSLEdit", OBJPROP_YDISTANCE, 70);
|
||||
ObjectSetInteger(m_chart_id, "ManualSLEdit", OBJPROP_XSIZE, 60);
|
||||
created_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("[ERROR] Failed to create ManualSLEdit. Error: ", GetLastError());
|
||||
}
|
||||
|
||||
// Manual Trade Button
|
||||
if(ObjectCreate(m_chart_id, "ManualTradeButton", OBJ_BUTTON, 0, 0, 0))
|
||||
{
|
||||
ObjectSetString(m_chart_id, "ManualTradeButton", OBJPROP_TEXT, "Open Market Order");
|
||||
ObjectSetInteger(m_chart_id, "ManualTradeButton", OBJPROP_CORNER, CORNER_RIGHT_UPPER);
|
||||
ObjectSetInteger(m_chart_id, "ManualTradeButton", OBJPROP_XDISTANCE, 110);
|
||||
ObjectSetInteger(m_chart_id, "ManualTradeButton", OBJPROP_YDISTANCE, 95);
|
||||
ObjectSetInteger(m_chart_id, "ManualTradeButton", OBJPROP_XSIZE, 130);
|
||||
ObjectSetInteger(m_chart_id, "ManualTradeButton", OBJPROP_YSIZE, 25);
|
||||
created_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("[ERROR] Failed to create ManualTradeButton. Error: ", GetLastError());
|
||||
}
|
||||
|
||||
if(m_enable_debug)
|
||||
{
|
||||
Print("[UIManager] Created ", created_count, " manual mode controls");
|
||||
}
|
||||
}
|
||||
};
|
||||
//+------------------------------------------------------------------+
|
||||
Reference in New Issue
Block a user