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