600 lines
22 KiB
MQL5
600 lines
22 KiB
MQL5
|
//+------------------------------------------------------------------+
|
||
|
//| ATR Dynamic Risk Manager.mq5 |
|
||
|
//| Optimized Forex ATR Risk Management System |
|
||
|
// The MQL5 language is used for programming trading strategies and indicators for the MetaTrader 5platform. The script you mentioned,
|
||
|
//"ATR Dynamic Risk Manager.mq5," seems to be a custom Expert Advisor (EA) or script designed to manage trading risk dynamically using the Average True Range (ATR) indicator.
|
||
|
//Below is a general outline of how you might implement such a script:
|
||
|
//
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| ATR Dynamic Risk Manager.mq5 |
|
||
|
//| Your Name or Company Name Here |
|
||
|
//| Your Website or Contact Info |
|
||
|
//+------------------------------------------------------------------+
|
||
|
#property strict
|
||
|
|
||
|
// Input parameters
|
||
|
input double RiskPercentage = 1.0; // Risk percentage per trade
|
||
|
input int ATRPeriod = 14; // ATR calculation period
|
||
|
input double ATRMultiplier = 1.5; // Multiplier for ATR to set stop loss
|
||
|
|
||
|
// Global variables
|
||
|
double atrValue;
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Expert initialization function |
|
||
|
//+------------------------------------------------------------------+
|
||
|
int OnInit()
|
||
|
{
|
||
|
// Initialization of the ATR indicator
|
||
|
atrValue = iATR(_Symbol, _Period, ATRPeriod);
|
||
|
|
||
|
if(atrValue <= 0)
|
||
|
{
|
||
|
Print("Error initializing ATR indicator");
|
||
|
return INIT_FAILED;
|
||
|
}
|
||
|
|
||
|
Print("ATR Dynamic Risk Manager initialized successfully");
|
||
|
return INIT_SUCCEEDED;
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Expert deinitialization function |
|
||
|
//+------------------------------------------------------------------+
|
||
|
void OnDeinit(const int reason)
|
||
|
{
|
||
|
// Cleanup code if necessary
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Expert tick function |
|
||
|
//+------------------------------------------------------------------+
|
||
|
void OnTick()
|
||
|
{
|
||
|
// Update ATR value on each tick
|
||
|
atrValue = iATR(_Symbol, _Period, ATRPeriod);
|
||
|
|
||
|
// Calculate the lot size based on risk percentage and ATR
|
||
|
double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
|
||
|
double riskAmount = accountBalance * (RiskPercentage / 100.0);
|
||
|
double stopLoss = atrValue * ATRMultiplier;
|
||
|
|
||
|
// Assuming a 1:1 risk-reward ratio for simplicity
|
||
|
double lotSize = CalculateLotSize(riskAmount, stopLoss);
|
||
|
|
||
|
// Example: Place a buy order with calculated lot size
|
||
|
if(/* Your buy condition here */)
|
||
|
{
|
||
|
// Check for existing positions or orders if necessary
|
||
|
// PlaceOrder function is a placeholder for your order logic
|
||
|
PlaceOrder(ORDER_TYPE_BUY, lotSize, stopLoss);
|
||
|
}
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Function to calculate lot size based on risk and stop loss |
|
||
|
//+------------------------------------------------------------------+
|
||
|
double CalculateLotSize(double riskAmount, double stopLoss)
|
||
|
{
|
||
|
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
|
||
|
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
|
||
|
|
||
|
if(tickValue <= 0 || tickSize <= 0)
|
||
|
{
|
||
|
Print("Error retrieving symbol information");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
double lotSize = riskAmount / (stopLoss / tickSize * tickValue);
|
||
|
return NormalizeDouble(lotSize, 2); // Adjust precision as needed
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Function to place an order |
|
||
|
//+------------------------------------------------------------------+
|
||
|
void PlaceOrder(int orderType, double lotSize, double stopLoss)
|
||
|
{
|
||
|
// Implement order placement logic here
|
||
|
// Example: OrderSend function with necessary parameters
|
||
|
// Ensure to handle errors and order confirmation
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|
||
|
|
||
|
//
|
||
|
// ### Key Components:
|
||
|
// 1. **ATR Calculation**: The script uses the ATR indicator to determine market volatility. The ATR value is updated on each tick.
|
||
|
//
|
||
|
// 2. **Risk Management**: The script calculates the lot size based on a specified risk percentage of the account balance and the ATR value, which is used to set the stop loss.
|
||
|
//
|
||
|
// 3. **Order Placement**: The script includes a placeholder for order placement logic. You need to implement the actual conditions for opening trades and manage existing positions.
|
||
|
//
|
||
|
// 4. **Error Handling**: Ensure to handle any errors, especially during order placement and ATR calculation.
|
||
|
//
|
||
|
// ### Notes:
|
||
|
// - This script is a template and requires further development to be functional. You need to define the specific trading conditions and complete the order placement logic.
|
||
|
// - Always test your scripts on a demo account before deploying them on a live account to ensure they work as expected.
|
||
|
// - Consider adding more features such as take profit levels, trailing stops, and additional risk management rules.
|
||
|
//
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
#property copyright "Copyright 2025"
|
||
|
#property link ""
|
||
|
#property version "1.00"
|
||
|
#property strict
|
||
|
#property description "Dynamic ATR-based risk management EA for forex"
|
||
|
|
||
|
#include <Trade\Trade.mqh>
|
||
|
#include <Trade\PositionInfo.mqh>
|
||
|
#include <Trade\OrderInfo.mqh>
|
||
|
|
||
|
// EA input parameters
|
||
|
input group "ATR Risk Parameters"
|
||
|
input int ATR_Period = 14; // ATR Period
|
||
|
input double SL_ATR_Multiplier = 3.0; // Stop Loss ATR Multiplier
|
||
|
input double TP_ATR_Multiplier = 3.5; // Take Profit ATR Multiplier
|
||
|
input double Trail_Start_Multiplier = 2.5; // Trailing Start ATR Multiplier
|
||
|
input double Trail_Step_Multiplier = 0.5; // Trailing Step ATR Multiplier
|
||
|
input int ATR_Shift = 1; // ATR shift (1 for previous bar)
|
||
|
input double Extra_Buffer_Pips = 5; // Extra buffer in pips
|
||
|
|
||
|
input group "Trading Parameters"
|
||
|
input bool Manage_SL = true; // Manage Stop Loss
|
||
|
input bool Manage_TP = true; // Manage Take Profit
|
||
|
input bool Use_Trailing_Stop = true; // Use Trailing Stop
|
||
|
input bool Manage_New_Positions = true; // Apply to new positions
|
||
|
input bool Manage_Existing_Positions = true; // Apply to existing positions
|
||
|
input int Magic_Number = 123456; // EA Magic Number (0 for all positions)
|
||
|
input bool Close_At_Profit_Target = false; // Close position at profit target instead of setting TP
|
||
|
|
||
|
input group "Timing Parameters"
|
||
|
input int Check_Interval_Seconds = 5; // Interval between checks (seconds)
|
||
|
input bool Process_On_Bar_Close = false; // Process only on bar close
|
||
|
|
||
|
// Global variables
|
||
|
CTrade trade;
|
||
|
CPositionInfo posInfo;
|
||
|
COrderInfo orderInfo;
|
||
|
double atrValue;
|
||
|
double point;
|
||
|
int digits;
|
||
|
datetime lastCheckTime = 0;
|
||
|
datetime lastBarTime = 0;
|
||
|
int atrHandle;
|
||
|
int positionCount = 0;
|
||
|
ulong positionTickets[];
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Expert initialization function |
|
||
|
//+------------------------------------------------------------------+
|
||
|
int OnInit()
|
||
|
{
|
||
|
// Initialize trade operations
|
||
|
trade.SetExpertMagicNumber(Magic_Number);
|
||
|
|
||
|
// Initialize market information
|
||
|
digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||
|
point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||
|
|
||
|
// Create ATR indicator handle
|
||
|
atrHandle = iATR(_Symbol, PERIOD_CURRENT, ATR_Period);
|
||
|
if(atrHandle == INVALID_HANDLE)
|
||
|
{
|
||
|
Print("Error creating ATR indicator handle");
|
||
|
return(INIT_FAILED);
|
||
|
}
|
||
|
|
||
|
// Set timer
|
||
|
if(!EventSetTimer(Check_Interval_Seconds))
|
||
|
{
|
||
|
Print("Error setting timer");
|
||
|
return(INIT_FAILED);
|
||
|
}
|
||
|
|
||
|
// Initial identification of positions
|
||
|
RefreshPositions();
|
||
|
|
||
|
Print("ATR Dynamic Risk Manager EA initialized successfully");
|
||
|
return(INIT_SUCCEEDED);
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Expert deinitialization function |
|
||
|
//+------------------------------------------------------------------+
|
||
|
void OnDeinit(const int reason)
|
||
|
{
|
||
|
// Release indicator handles
|
||
|
IndicatorRelease(atrHandle);
|
||
|
|
||
|
// Remove timer
|
||
|
EventKillTimer();
|
||
|
|
||
|
// Clean up
|
||
|
Comment("");
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Timer function |
|
||
|
//+------------------------------------------------------------------+
|
||
|
void OnTimer()
|
||
|
{
|
||
|
// Check if it's time to update
|
||
|
if(!IsTimeToCheck())
|
||
|
return;
|
||
|
|
||
|
// Update ATR value and manage positions
|
||
|
UpdateATRValue();
|
||
|
ManagePositions();
|
||
|
|
||
|
// Record the check time
|
||
|
lastCheckTime = TimeCurrent();
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Expert tick function |
|
||
|
//+------------------------------------------------------------------+
|
||
|
void OnTick()
|
||
|
{
|
||
|
// Check if we process only on bar close
|
||
|
if(Process_On_Bar_Close)
|
||
|
{
|
||
|
datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
|
||
|
if(currentBarTime != lastBarTime)
|
||
|
{
|
||
|
lastBarTime = currentBarTime;
|
||
|
UpdateATRValue();
|
||
|
ManagePositions();
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Otherwise, check if it's time to update based on timer
|
||
|
if(IsTimeToCheck())
|
||
|
{
|
||
|
UpdateATRValue();
|
||
|
ManagePositions();
|
||
|
lastCheckTime = TimeCurrent();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Check if it's time to process |
|
||
|
//+------------------------------------------------------------------+
|
||
|
bool IsTimeToCheck()
|
||
|
{
|
||
|
if(TimeCurrent() - lastCheckTime >= Check_Interval_Seconds)
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Update ATR value |
|
||
|
//+------------------------------------------------------------------+
|
||
|
void UpdateATRValue()
|
||
|
{
|
||
|
double atrBuffer[];
|
||
|
|
||
|
// Get current ATR value
|
||
|
if(CopyBuffer(atrHandle, 0, ATR_Shift, 1, atrBuffer) > 0)
|
||
|
{
|
||
|
atrValue = NormalizeDouble(atrBuffer[0], digits);
|
||
|
|
||
|
if(atrValue <= 0)
|
||
|
{
|
||
|
Print("Warning: ATR calculation returned zero or negative value. Using fallback value.");
|
||
|
atrValue = 0.0001; // Fallback to prevent errors
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Print("Error getting ATR value: ", GetLastError());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Refresh list of position tickets |
|
||
|
//+------------------------------------------------------------------+
|
||
|
void RefreshPositions()
|
||
|
{
|
||
|
int total = PositionsTotal();
|
||
|
int count = 0;
|
||
|
|
||
|
// First, count valid positions
|
||
|
for(int i = 0; i < total; i++)
|
||
|
{
|
||
|
if(posInfo.SelectByIndex(i))
|
||
|
{
|
||
|
if(Magic_Number == 0 || posInfo.Magic() == Magic_Number)
|
||
|
{
|
||
|
if(posInfo.Symbol() == _Symbol)
|
||
|
count++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Resize array and fill with position tickets
|
||
|
ArrayResize(positionTickets, count);
|
||
|
positionCount = 0;
|
||
|
|
||
|
for(int i = 0; i < total; i++)
|
||
|
{
|
||
|
if(posInfo.SelectByIndex(i))
|
||
|
{
|
||
|
if(Magic_Number == 0 || posInfo.Magic() == Magic_Number)
|
||
|
{
|
||
|
if(posInfo.Symbol() == _Symbol)
|
||
|
{
|
||
|
positionTickets[positionCount] = posInfo.Ticket();
|
||
|
positionCount++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Manage all positions |
|
||
|
//+------------------------------------------------------------------+
|
||
|
void ManagePositions()
|
||
|
{
|
||
|
// Get current open positions
|
||
|
RefreshPositions();
|
||
|
|
||
|
// Convert buffer from pips to points
|
||
|
double bufferInPoints = Extra_Buffer_Pips * 10 * point;
|
||
|
|
||
|
// Display current ATR info
|
||
|
string info = "Current ATR: " + DoubleToString(atrValue, digits) + "\n";
|
||
|
info += "SL distance: " + DoubleToString(atrValue * SL_ATR_Multiplier + bufferInPoints, digits) + "\n";
|
||
|
info += "TP distance: " + DoubleToString(atrValue * TP_ATR_Multiplier, digits) + "\n";
|
||
|
info += "Positions managed: " + IntegerToString(positionCount);
|
||
|
Comment(info);
|
||
|
|
||
|
// Process existing positions
|
||
|
if(Manage_Existing_Positions)
|
||
|
{
|
||
|
for(int i = 0; i < positionCount; i++)
|
||
|
{
|
||
|
if(posInfo.SelectByTicket(positionTickets[i]))
|
||
|
{
|
||
|
// Process the position
|
||
|
ProcessPosition(posInfo.PositionType(), posInfo.PriceOpen(),
|
||
|
posInfo.StopLoss(), posInfo.TakeProfit(),
|
||
|
posInfo.Ticket(), bufferInPoints);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Process pending orders if needed
|
||
|
if(Manage_New_Positions)
|
||
|
ProcessPendingOrders(bufferInPoints);
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Process pending orders |
|
||
|
//+------------------------------------------------------------------+
|
||
|
void ProcessPendingOrders(double bufferInPoints)
|
||
|
{
|
||
|
for(int i = OrdersTotal() - 1; i >= 0; i--)
|
||
|
{
|
||
|
if(orderInfo.SelectByIndex(i))
|
||
|
{
|
||
|
// Check if it's our order and symbol
|
||
|
if((Magic_Number == 0 || orderInfo.Magic() == Magic_Number) &&
|
||
|
orderInfo.Symbol() == _Symbol)
|
||
|
{
|
||
|
// Get order type
|
||
|
ENUM_ORDER_TYPE orderType = orderInfo.OrderType();
|
||
|
|
||
|
// Process only if it's a pending order
|
||
|
if(orderType == ORDER_TYPE_BUY_LIMIT || orderType == ORDER_TYPE_BUY_STOP ||
|
||
|
orderType == ORDER_TYPE_SELL_LIMIT || orderType == ORDER_TYPE_SELL_STOP)
|
||
|
{
|
||
|
double newSL = 0.0, newTP = 0.0;
|
||
|
double entryPrice = orderInfo.PriceOpen();
|
||
|
|
||
|
// Calculate SL and TP based on order type
|
||
|
if(orderType == ORDER_TYPE_BUY_LIMIT || orderType == ORDER_TYPE_BUY_STOP)
|
||
|
{
|
||
|
if(Manage_SL)
|
||
|
newSL = NormalizeDouble(entryPrice - (atrValue * SL_ATR_Multiplier) - bufferInPoints, digits);
|
||
|
|
||
|
if(Manage_TP)
|
||
|
newTP = NormalizeDouble(entryPrice + (atrValue * TP_ATR_Multiplier), digits);
|
||
|
}
|
||
|
else // Sell orders
|
||
|
{
|
||
|
if(Manage_SL)
|
||
|
newSL = NormalizeDouble(entryPrice + (atrValue * SL_ATR_Multiplier) + bufferInPoints, digits);
|
||
|
|
||
|
if(Manage_TP)
|
||
|
newTP = NormalizeDouble(entryPrice - (atrValue * TP_ATR_Multiplier), digits);
|
||
|
}
|
||
|
|
||
|
// Get current SL and TP
|
||
|
double currentSL = orderInfo.StopLoss();
|
||
|
double currentTP = orderInfo.TakeProfit();
|
||
|
|
||
|
// Check if we need to modify the order
|
||
|
if((currentSL != newSL && Manage_SL) || (currentTP != newTP && Manage_TP))
|
||
|
{
|
||
|
if(!Manage_SL) newSL = currentSL; // Keep current SL if not managing it
|
||
|
if(!Manage_TP) newTP = currentTP; // Keep current TP if not managing it
|
||
|
|
||
|
// Modify the order
|
||
|
if(!trade.OrderModify(orderInfo.Ticket(), entryPrice, newSL, newTP, orderInfo.TypeTime(), orderInfo.TimeExpiration()))
|
||
|
{
|
||
|
Print("Failed to modify order #", orderInfo.Ticket(), " Error: ", GetLastError());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Process a single position |
|
||
|
//+------------------------------------------------------------------+
|
||
|
void ProcessPosition(ENUM_POSITION_TYPE posType, double openPrice,
|
||
|
double currentSL, double currentTP, ulong ticket,
|
||
|
double bufferInPoints)
|
||
|
{
|
||
|
double newSL = 0, newTP = 0;
|
||
|
double trailingStartLevel = 0;
|
||
|
double currentPrice = 0;
|
||
|
bool modifyPosition = false;
|
||
|
|
||
|
// Get current bid/ask
|
||
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||
|
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||
|
|
||
|
// Set SL and TP based on position type
|
||
|
if(posType == POSITION_TYPE_BUY)
|
||
|
{
|
||
|
currentPrice = bid;
|
||
|
|
||
|
// Calculate new stop loss if needed
|
||
|
if(Manage_SL)
|
||
|
{
|
||
|
// Initial SL calculation
|
||
|
newSL = NormalizeDouble(openPrice - (atrValue * SL_ATR_Multiplier) - bufferInPoints, digits);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
newSL = currentSL; // Keep current SL
|
||
|
}
|
||
|
|
||
|
// Calculate new take profit if needed
|
||
|
if(Manage_TP && !Close_At_Profit_Target)
|
||
|
{
|
||
|
newTP = NormalizeDouble(openPrice + (atrValue * TP_ATR_Multiplier), digits);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
newTP = currentTP; // Keep current TP
|
||
|
}
|
||
|
|
||
|
// Check for profit target if closing instead of TP
|
||
|
if(Close_At_Profit_Target)
|
||
|
{
|
||
|
double profitTarget = openPrice + (atrValue * TP_ATR_Multiplier);
|
||
|
if(currentPrice >= profitTarget)
|
||
|
{
|
||
|
// Close the position at profit target
|
||
|
if(trade.PositionClose(ticket))
|
||
|
{
|
||
|
Print("Position #", ticket, " closed at profit target!");
|
||
|
}
|
||
|
return; // Exit the function
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check for trailing stop if enabled
|
||
|
if(Use_Trailing_Stop && Manage_SL)
|
||
|
{
|
||
|
// Calculate trailing start level
|
||
|
trailingStartLevel = NormalizeDouble(openPrice + (atrValue * Trail_Start_Multiplier), digits);
|
||
|
|
||
|
// Check if price has moved enough to activate trailing
|
||
|
if(currentPrice >= trailingStartLevel)
|
||
|
{
|
||
|
// Calculate new trailing stop level
|
||
|
double trailingSL = NormalizeDouble(currentPrice - (atrValue * Trail_Step_Multiplier) - bufferInPoints, digits);
|
||
|
|
||
|
// Only move stop up, never down
|
||
|
if(trailingSL > currentSL || currentSL == 0)
|
||
|
{
|
||
|
newSL = trailingSL;
|
||
|
modifyPosition = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if(posType == POSITION_TYPE_SELL)
|
||
|
{
|
||
|
currentPrice = ask;
|
||
|
|
||
|
// Calculate new stop loss if needed
|
||
|
if(Manage_SL)
|
||
|
{
|
||
|
// Initial SL calculation
|
||
|
newSL = NormalizeDouble(openPrice + (atrValue * SL_ATR_Multiplier) + bufferInPoints, digits);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
newSL = currentSL; // Keep current SL
|
||
|
}
|
||
|
|
||
|
// Calculate new take profit if needed
|
||
|
if(Manage_TP && !Close_At_Profit_Target)
|
||
|
{
|
||
|
newTP = NormalizeDouble(openPrice - (atrValue * TP_ATR_Multiplier), digits);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
newTP = currentTP; // Keep current TP
|
||
|
}
|
||
|
|
||
|
// Check for profit target if closing instead of TP
|
||
|
if(Close_At_Profit_Target)
|
||
|
{
|
||
|
double profitTarget = openPrice - (atrValue * TP_ATR_Multiplier);
|
||
|
if(currentPrice <= profitTarget)
|
||
|
{
|
||
|
// Close the position at profit target
|
||
|
if(trade.PositionClose(ticket))
|
||
|
{
|
||
|
Print("Position #", ticket, " closed at profit target!");
|
||
|
}
|
||
|
return; // Exit the function
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check for trailing stop if enabled
|
||
|
if(Use_Trailing_Stop && Manage_SL)
|
||
|
{
|
||
|
// Calculate trailing start level
|
||
|
trailingStartLevel = NormalizeDouble(openPrice - (atrValue * Trail_Start_Multiplier), digits);
|
||
|
|
||
|
// Check if price has moved enough to activate trailing
|
||
|
if(currentPrice <= trailingStartLevel)
|
||
|
{
|
||
|
// Calculate new trailing stop level
|
||
|
double trailingSL = NormalizeDouble(currentPrice + (atrValue * Trail_Step_Multiplier) + bufferInPoints, digits);
|
||
|
|
||
|
// Only move stop down (for shorts), never up
|
||
|
if(trailingSL < currentSL || currentSL == 0)
|
||
|
{
|
||
|
newSL = trailingSL;
|
||
|
modifyPosition = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Ensure we're not moving SL in wrong direction for position type
|
||
|
if(posType == POSITION_TYPE_BUY && currentSL > 0 && newSL < currentSL)
|
||
|
{
|
||
|
// Keep current SL
|
||
|
newSL = currentSL;
|
||
|
}
|
||
|
else if(posType == POSITION_TYPE_SELL && currentSL > 0 && newSL > currentSL)
|
||
|
{
|
||
|
// Keep current SL
|
||
|
newSL = currentSL;
|
||
|
}
|
||
|
|
||
|
// Check if SL or TP needs to be changed
|
||
|
if((MathAbs(currentSL - newSL) > Point() && Manage_SL) ||
|
||
|
(MathAbs(currentTP - newTP) > Point() && Manage_TP) ||
|
||
|
modifyPosition)
|
||
|
{
|
||
|
// Apply the new SL and TP
|
||
|
if(trade.PositionModify(ticket, newSL, newTP))
|
||
|
{
|
||
|
string action = modifyPosition ? "Trailing Stop" : "Initial SL/TP";
|
||
|
Print(action, " applied to position #", ticket, " New SL: ", newSL, " New TP: ", newTP);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Print("Failed to modify position #", ticket, " Error: ", GetLastError());
|
||
|
}
|
||
|
}
|
||
|
}
|