Files
EA/Buffer EA/Include/RiskManager.mqh
Kunthawat Greethong 04aa2eb2e6 New EA and Indi
2026-01-25 10:34:54 +07:00

575 lines
18 KiB
Plaintext

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