New EA and Indi

This commit is contained in:
Kunthawat Greethong
2026-01-25 10:34:54 +07:00
parent 39ce46877e
commit 04aa2eb2e6
37 changed files with 17051 additions and 0 deletions

View 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);
}
}
};
//+------------------------------------------------------------------+