709 lines
22 KiB
Plaintext
709 lines
22 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| TrendPullbackPro.mq5|
|
|
//| Trend-following pullback EA with risk & management |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Hadi Strategy"
|
|
#property link ""
|
|
#property version "1.10"
|
|
#property strict
|
|
|
|
//--- Inputs: risk & money management
|
|
input double InpRiskPerTradePercent = 1.0; // Total risk per trade (% of balance)
|
|
input int InpMagicNumber = 123456; // Magic number
|
|
input double InpStopBufferPoints = 10; // Extra buffer beyond swing high/low (points)
|
|
|
|
//--- Inputs: indicators
|
|
input ENUM_TIMEFRAMES InpTF = PERIOD_CURRENT; // Signal timeframe
|
|
input int InpFastEMAPeriod = 20;
|
|
input int InpSlowEMAPeriod = 50;
|
|
input int InpRSIPeriod = 14;
|
|
input double InpRSILowZoneMin = 40; // Uptrend pullback zone min
|
|
input double InpRSILowZoneMax = 50; // Uptrend pullback zone max
|
|
input double InpRSIHighZoneMin = 50; // Downtrend pullback zone min
|
|
input double InpRSIHighZoneMax = 60; // Downtrend pullback zone max
|
|
|
|
//--- Inputs: partial TP and R multiples
|
|
input bool InpUsePartialTP = true;
|
|
input double InpPartialCloseAtR = 1.0; // R at which to take partial profit
|
|
input double InpPartialClosePercent = 50.0; // % of volume to close at partial TP
|
|
input double InpFinalTargetR = 3.0; // Final target R for runner
|
|
input bool InpUseBreakEven = true;
|
|
input double InpBreakEvenRMultiple = 1.0; // Move SL to BE at this R (runner)
|
|
|
|
//--- Inputs: trailing stop
|
|
input bool InpUseEMATrailing = true;
|
|
input double InpTrailStartR = 1.0; // Start trailing after this R
|
|
input double InpTrailOffsetPoints = 5; // Extra buffer beyond EMA (points)
|
|
|
|
//--- Inputs: spread & time filters
|
|
input bool InpUseSpreadFilter = true;
|
|
input double InpMaxSpreadPoints = 30; // Max allowed spread (in points)
|
|
|
|
input bool InpUseTimeFilter = false;
|
|
input int InpSessionStartHour = 8; // Trading session start (server time)
|
|
input int InpSessionEndHour = 22; // Trading session end (server time)
|
|
input bool InpAvoidRolloverHour = true;
|
|
input int InpRolloverHour = 23; // Avoid new entries at this hour
|
|
|
|
//--- constants
|
|
#define NO_TREND 0
|
|
#define UPTREND 1
|
|
#define DOWNTREND -1
|
|
|
|
#define PARTIAL_COMMENT "TrendPullbackEA_Partial"
|
|
#define RUNNER_COMMENT "TrendPullbackEA_Runner"
|
|
|
|
//--- Global indicator handles
|
|
int rsi_handle;
|
|
int ema_fast_handle;
|
|
int ema_slow_handle;
|
|
|
|
//--- Tick storage
|
|
MqlTick last_tick;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| OnInit |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
rsi_handle = iRSI(_Symbol, InpTF, InpRSIPeriod, PRICE_CLOSE);
|
|
if(rsi_handle == INVALID_HANDLE)
|
|
{
|
|
Print("Error creating RSI handle. Error: ", GetLastError());
|
|
return(INIT_FAILED);
|
|
}
|
|
|
|
ema_fast_handle = iMA(_Symbol, InpTF, InpFastEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
|
|
if(ema_fast_handle == INVALID_HANDLE)
|
|
{
|
|
Print("Error creating fast EMA handle. Error: ", GetLastError());
|
|
return(INIT_FAILED);
|
|
}
|
|
|
|
ema_slow_handle = iMA(_Symbol, InpTF, InpSlowEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
|
|
if(ema_slow_handle == INVALID_HANDLE)
|
|
{
|
|
Print("Error creating slow EMA handle. Error: ", GetLastError());
|
|
return(INIT_FAILED);
|
|
}
|
|
|
|
SymbolInfoTick(_Symbol, last_tick);
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| OnDeinit |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
IndicatorRelease(rsi_handle);
|
|
IndicatorRelease(ema_fast_handle);
|
|
IndicatorRelease(ema_slow_handle);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| OnTick |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
if(!SymbolInfoTick(_Symbol, last_tick))
|
|
return;
|
|
|
|
// Run main logic once per bar on signal timeframe
|
|
static datetime last_bar_time = 0;
|
|
datetime cur_bar_time = iTime(_Symbol, InpTF, 0);
|
|
if(cur_bar_time == last_bar_time)
|
|
return;
|
|
last_bar_time = cur_bar_time;
|
|
|
|
ManageOpenPositions();
|
|
|
|
// Avoid new entries if no time/spread conditions
|
|
if(!CanOpenNewTrades())
|
|
return;
|
|
|
|
CheckOpenSignals();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if we can open new trades (time & spread) |
|
|
//+------------------------------------------------------------------+
|
|
bool CanOpenNewTrades()
|
|
{
|
|
// Spread filter
|
|
if(InpUseSpreadFilter)
|
|
{
|
|
double spread_points = (SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID))
|
|
/ SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
|
if(spread_points > InpMaxSpreadPoints)
|
|
{
|
|
//Print("Spread too high: ", spread_points, " > ", InpMaxSpreadPoints);
|
|
return(false);
|
|
}
|
|
}
|
|
|
|
// Time filter
|
|
if(InpUseTimeFilter)
|
|
{
|
|
datetime now = TimeCurrent();
|
|
MqlDateTime dt;
|
|
TimeToStruct(now, dt);
|
|
|
|
// avoid rollover hour
|
|
if(InpAvoidRolloverHour && dt.hour == InpRolloverHour)
|
|
return(false);
|
|
|
|
// session window
|
|
bool inSession = (dt.hour >= InpSessionStartHour && dt.hour < InpSessionEndHour);
|
|
if(!inSession)
|
|
return(false);
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check for new entry signals |
|
|
//+------------------------------------------------------------------+
|
|
void CheckOpenSignals()
|
|
{
|
|
if(HasOpenPosition())
|
|
return;
|
|
|
|
int trend = GetTrendDirection();
|
|
if(trend == NO_TREND)
|
|
return;
|
|
|
|
double rsi_value;
|
|
if(!GetRSI(1, rsi_value))
|
|
return;
|
|
|
|
MqlRates rates[];
|
|
if(CopyRates(_Symbol, InpTF, 0, 3, rates) < 3)
|
|
return;
|
|
// rates[0] current, [1] closed 1, [2] closed 2
|
|
MqlRates candle = rates[1];
|
|
MqlRates prev = rates[2];
|
|
|
|
bool longSignal = false;
|
|
bool shortSignal = false;
|
|
|
|
if(trend == UPTREND)
|
|
{
|
|
if(rsi_value >= InpRSILowZoneMin && rsi_value <= InpRSILowZoneMax)
|
|
{
|
|
if(IsBullishPattern(candle, prev))
|
|
longSignal = true;
|
|
}
|
|
}
|
|
else if(trend == DOWNTREND)
|
|
{
|
|
if(rsi_value >= InpRSIHighZoneMin && rsi_value <= InpRSIHighZoneMax)
|
|
{
|
|
if(IsBearishPattern(candle, prev))
|
|
shortSignal = true;
|
|
}
|
|
}
|
|
|
|
if(longSignal)
|
|
OpenTrade(ORDER_TYPE_BUY, candle);
|
|
else if(shortSignal)
|
|
OpenTrade(ORDER_TYPE_SELL, candle);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Manage open positions (partial TP, BE, trailing) |
|
|
//+------------------------------------------------------------------+
|
|
void ManageOpenPositions()
|
|
{
|
|
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
|
|
|
for(int i = PositionsTotal()-1; i >= 0; i--)
|
|
{
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(!PositionSelectByTicket(ticket))
|
|
continue;
|
|
|
|
if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber)
|
|
continue;
|
|
if(PositionGetString(POSITION_SYMBOL) != _Symbol)
|
|
continue;
|
|
|
|
long type = PositionGetInteger(POSITION_TYPE);
|
|
double volume = PositionGetDouble(POSITION_VOLUME);
|
|
double sl = PositionGetDouble(POSITION_SL);
|
|
double tp = PositionGetDouble(POSITION_TP);
|
|
double price_open = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
string comment = PositionGetString(POSITION_COMMENT);
|
|
|
|
MqlTick tick;
|
|
SymbolInfoTick(_Symbol, tick);
|
|
double current_price = (type == POSITION_TYPE_BUY ? tick.bid : tick.ask);
|
|
|
|
double riskPoints;
|
|
if(type == POSITION_TYPE_BUY)
|
|
riskPoints = (price_open - sl) / point;
|
|
else
|
|
riskPoints = (sl - price_open) / point;
|
|
|
|
if(riskPoints <= 0)
|
|
continue;
|
|
|
|
double profitPoints;
|
|
if(type == POSITION_TYPE_BUY)
|
|
profitPoints = (current_price - price_open) / point;
|
|
else
|
|
profitPoints = (price_open - current_price) / point;
|
|
|
|
double rMultiple = profitPoints / riskPoints;
|
|
|
|
// 1) Partial TP logic for "partial" leg
|
|
if(InpUsePartialTP && comment == PARTIAL_COMMENT)
|
|
{
|
|
if(rMultiple >= InpPartialCloseAtR)
|
|
{
|
|
// Close this position fully (it's the partial leg)
|
|
ClosePosition(ticket);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// 2) Break-even & trailing for runner leg
|
|
if(comment == RUNNER_COMMENT)
|
|
{
|
|
// Break-even
|
|
if(InpUseBreakEven && rMultiple >= InpBreakEvenRMultiple)
|
|
{
|
|
if(type == POSITION_TYPE_BUY && sl < price_open)
|
|
ModifyPositionSL(ticket, price_open);
|
|
else if(type == POSITION_TYPE_SELL && sl > price_open)
|
|
ModifyPositionSL(ticket, price_open);
|
|
}
|
|
|
|
// EMA trailing after a certain R
|
|
if(InpUseEMATrailing && rMultiple >= InpTrailStartR)
|
|
{
|
|
double emaFast;
|
|
if(!GetEMA(ema_fast_handle, 1, emaFast))
|
|
continue;
|
|
|
|
double newSL;
|
|
double offset = InpTrailOffsetPoints * point;
|
|
if(type == POSITION_TYPE_BUY)
|
|
{
|
|
newSL = emaFast - offset;
|
|
if(newSL > sl && newSL < current_price) // only tighten SL
|
|
ModifyPositionSL(ticket, newSL);
|
|
}
|
|
else
|
|
{
|
|
newSL = emaFast + offset;
|
|
if(newSL < sl && newSL > current_price)
|
|
ModifyPositionSL(ticket, newSL);
|
|
}
|
|
}
|
|
|
|
// Optionally: ensure TP aligns with final target R
|
|
// If user wants to rely only on trailing, they can disable or adjust final R.
|
|
// We keep TP as set at entry.
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get trend direction from EMAs |
|
|
//+------------------------------------------------------------------+
|
|
int GetTrendDirection()
|
|
{
|
|
double ema_fast[3], ema_slow[3];
|
|
if(CopyBuffer(ema_fast_handle, 0, 0, 3, ema_fast) < 3)
|
|
return NO_TREND;
|
|
if(CopyBuffer(ema_slow_handle, 0, 0, 3, ema_slow) < 3)
|
|
return NO_TREND;
|
|
|
|
double fast = ema_fast[1];
|
|
double slow = ema_slow[1];
|
|
|
|
if(fast > slow)
|
|
return UPTREND;
|
|
else if(fast < slow)
|
|
return DOWNTREND;
|
|
return NO_TREND;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get RSI value at bar index |
|
|
//+------------------------------------------------------------------+
|
|
bool GetRSI(int index, double &value)
|
|
{
|
|
double buffer[1];
|
|
int copied = CopyBuffer(rsi_handle, 0, index, 1, buffer);
|
|
if(copied != 1)
|
|
return false;
|
|
|
|
value = buffer[0];
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get EMA value from handle |
|
|
//+------------------------------------------------------------------+
|
|
bool GetEMA(int handle, int index, double &value)
|
|
{
|
|
double buffer[1];
|
|
int copied = CopyBuffer(handle, 0, index, 1, buffer);
|
|
if(copied != 1)
|
|
return false;
|
|
value = buffer[0];
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Bullish pattern: engulfing + pin-bar-ish |
|
|
//+------------------------------------------------------------------+
|
|
bool IsBullishPattern(const MqlRates &c, const MqlRates &prev)
|
|
{
|
|
double bodyC = MathAbs(c.close - c.open);
|
|
double bodyP = MathAbs(prev.close - prev.open);
|
|
double rangeC = c.high - c.low;
|
|
|
|
if(rangeC <= 0)
|
|
return false;
|
|
|
|
// Basic bullish: close > open
|
|
if(c.close <= c.open)
|
|
return false;
|
|
|
|
// Engulfing body: current body >= previous body, and covers its open/close
|
|
bool engulfing = (bodyC >= bodyP && c.open <= prev.close && c.close >= prev.open);
|
|
|
|
// Pin-bar-ish: long lower wick
|
|
double lowerWick = MathMin(c.open, c.close) - c.low;
|
|
bool longLowerWick = (lowerWick >= rangeC * 0.4); // tweak
|
|
|
|
if(engulfing || longLowerWick)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Bearish pattern: engulfing + pin-bar-ish |
|
|
//+------------------------------------------------------------------+
|
|
bool IsBearishPattern(const MqlRates &c, const MqlRates &prev)
|
|
{
|
|
double bodyC = MathAbs(c.close - c.open);
|
|
double bodyP = MathAbs(prev.close - prev.open);
|
|
double rangeC = c.high - c.low;
|
|
|
|
if(rangeC <= 0)
|
|
return false;
|
|
|
|
// Basic bearish: close < open
|
|
if(c.close >= c.open)
|
|
return false;
|
|
|
|
// Engulfing body
|
|
bool engulfing = (bodyC >= bodyP && c.open >= prev.close && c.close <= prev.open);
|
|
|
|
// Pin-bar-ish: long upper wick
|
|
double upperWick = c.high - MathMax(c.open, c.close);
|
|
bool longUpperWick = (upperWick >= rangeC * 0.4); // tweak
|
|
|
|
if(engulfing || longUpperWick)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if there is already an open position |
|
|
//+------------------------------------------------------------------+
|
|
bool HasOpenPosition()
|
|
{
|
|
for(int i = PositionsTotal()-1; i >= 0; i--)
|
|
{
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(!PositionSelectByTicket(ticket))
|
|
continue;
|
|
|
|
if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber)
|
|
continue;
|
|
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate lot size based on total risk % |
|
|
//+------------------------------------------------------------------+
|
|
double CalculateTotalVolume(double stopLossPrice, ENUM_ORDER_TYPE type)
|
|
{
|
|
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
double riskMoney = balance * InpRiskPerTradePercent / 100.0;
|
|
|
|
double entryPrice = (type == ORDER_TYPE_BUY ? last_tick.ask : last_tick.bid);
|
|
|
|
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
|
double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
|
|
double tick_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
|
|
|
|
if(tick_size <= 0 || tick_value <= 0)
|
|
return(0.0);
|
|
|
|
double slDistance = (type == ORDER_TYPE_BUY ? entryPrice - stopLossPrice : stopLossPrice - entryPrice);
|
|
if(slDistance <= 0)
|
|
return(0.0);
|
|
|
|
double moneyPerPointPerLot = tick_value / tick_size * point;
|
|
double stopPoints = slDistance / point;
|
|
|
|
double volume = riskMoney / (stopPoints * moneyPerPointPerLot);
|
|
|
|
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
|
|
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
|
|
double lotStep= SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
|
|
|
|
volume = MathFloor(volume / lotStep) * lotStep;
|
|
|
|
if(volume < minLot)
|
|
volume = 0.0;
|
|
if(volume > maxLot)
|
|
volume = maxLot;
|
|
|
|
return(volume);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Open trade: split into partial + runner |
|
|
//+------------------------------------------------------------------+
|
|
void OpenTrade(ENUM_ORDER_TYPE type, const MqlRates &signalCandle)
|
|
{
|
|
MqlTick tick;
|
|
if(!SymbolInfoTick(_Symbol, tick))
|
|
return;
|
|
|
|
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
|
double entryPrice = (type == ORDER_TYPE_BUY ? tick.ask : tick.bid);
|
|
|
|
double slPrice;
|
|
if(type == ORDER_TYPE_BUY)
|
|
slPrice = signalCandle.low - InpStopBufferPoints * point;
|
|
else
|
|
slPrice = signalCandle.high + InpStopBufferPoints * point;
|
|
|
|
if(slPrice <= 0)
|
|
return;
|
|
|
|
double totalVolume = CalculateTotalVolume(slPrice, type);
|
|
if(totalVolume <= 0)
|
|
{
|
|
Print("Calculated total volume <= 0, skipping trade.");
|
|
return;
|
|
}
|
|
|
|
double slDistance = (type == ORDER_TYPE_BUY ? entryPrice - slPrice : slPrice - entryPrice);
|
|
|
|
// final TP at FinalTargetR
|
|
double finalTPDistance = slDistance * InpFinalTargetR;
|
|
double runnerTP;
|
|
if(type == ORDER_TYPE_BUY)
|
|
runnerTP = entryPrice + finalTPDistance;
|
|
else
|
|
runnerTP = entryPrice - finalTPDistance;
|
|
|
|
// partial volume
|
|
double partialVolume = totalVolume;
|
|
double runnerVolume = 0.0;
|
|
|
|
if(InpUsePartialTP && InpPartialClosePercent > 0 && InpPartialClosePercent < 100)
|
|
{
|
|
partialVolume = totalVolume * (InpPartialClosePercent / 100.0);
|
|
runnerVolume = totalVolume - partialVolume;
|
|
}
|
|
else
|
|
{
|
|
// no partial: everything is runner
|
|
partialVolume = 0.0;
|
|
runnerVolume = totalVolume;
|
|
}
|
|
|
|
// normalized volumes
|
|
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
|
|
partialVolume = MathFloor(partialVolume / lotStep) * lotStep;
|
|
runnerVolume = MathFloor(runnerVolume / lotStep) * lotStep;
|
|
|
|
if(partialVolume < SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN))
|
|
partialVolume = 0.0;
|
|
if(runnerVolume < SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN))
|
|
runnerVolume = 0.0;
|
|
|
|
// Partial TP price at PartialCloseAtR
|
|
double partialTPPrice = 0.0;
|
|
if(InpUsePartialTP && partialVolume > 0 && InpPartialCloseAtR > 0)
|
|
{
|
|
double partialTPDist = slDistance * InpPartialCloseAtR;
|
|
if(type == ORDER_TYPE_BUY)
|
|
partialTPPrice = entryPrice + partialTPDist;
|
|
else
|
|
partialTPPrice = entryPrice - partialTPDist;
|
|
}
|
|
|
|
// 1) Open partial leg
|
|
if(partialVolume > 0.0)
|
|
{
|
|
SendOrder(type, partialVolume, slPrice, partialTPPrice, PARTIAL_COMMENT);
|
|
}
|
|
|
|
// 2) Open runner leg
|
|
if(runnerVolume > 0.0)
|
|
{
|
|
SendOrder(type, runnerVolume, slPrice, runnerTP, RUNNER_COMMENT);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Send market order |
|
|
//+------------------------------------------------------------------+
|
|
bool SendOrder(ENUM_ORDER_TYPE type, double volume, double sl, double tp, string comment)
|
|
{
|
|
if(volume <= 0.0)
|
|
return(false);
|
|
|
|
MqlTick tick;
|
|
if(!SymbolInfoTick(_Symbol, tick))
|
|
return(false);
|
|
|
|
MqlTradeRequest request;
|
|
MqlTradeResult result;
|
|
ZeroMemory(request);
|
|
ZeroMemory(result);
|
|
|
|
request.action = TRADE_ACTION_DEAL;
|
|
request.magic = InpMagicNumber;
|
|
request.symbol = _Symbol;
|
|
request.volume = volume;
|
|
request.type = type;
|
|
request.deviation = 20; // points
|
|
request.type_filling= ORDER_FILLING_FOK;
|
|
request.price = (type == ORDER_TYPE_BUY ? tick.ask : tick.bid);
|
|
request.sl = sl;
|
|
request.tp = tp;
|
|
request.comment = comment;
|
|
|
|
if(!OrderSend(request, result))
|
|
{
|
|
Print("OrderSend failed. Error: ", GetLastError());
|
|
return(false);
|
|
}
|
|
if(result.retcode != TRADE_RETCODE_DONE)
|
|
{
|
|
Print("OrderSend retcode: ", result.retcode);
|
|
return(false);
|
|
}
|
|
|
|
Print("Opened ", (type == ORDER_TYPE_BUY ? "BUY" : "SELL"),
|
|
" ticket #", result.order, " vol=", DoubleToString(volume,2),
|
|
" SL=", DoubleToString(sl,_Digits)," TP=", DoubleToString(tp,_Digits),
|
|
" comment=", comment);
|
|
return(true);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Modify SL for position |
|
|
//+------------------------------------------------------------------+
|
|
bool ModifyPositionSL(ulong ticket, double newSL)
|
|
{
|
|
if(!PositionSelectByTicket(ticket))
|
|
return(false);
|
|
|
|
string sym = PositionGetString(POSITION_SYMBOL);
|
|
long type = PositionGetInteger(POSITION_TYPE);
|
|
double volume = PositionGetDouble(POSITION_VOLUME);
|
|
double price = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
double tp = PositionGetDouble(POSITION_TP);
|
|
|
|
MqlTradeRequest request;
|
|
MqlTradeResult result;
|
|
ZeroMemory(request);
|
|
ZeroMemory(result);
|
|
|
|
request.action = TRADE_ACTION_SLTP;
|
|
request.magic = InpMagicNumber;
|
|
request.symbol = sym;
|
|
request.volume = volume;
|
|
request.type = (ENUM_ORDER_TYPE)type;
|
|
request.price = price;
|
|
request.sl = newSL;
|
|
request.tp = tp;
|
|
|
|
if(!OrderSend(request, result))
|
|
{
|
|
Print("ModifyPositionSL OrderSend failed. Error: ", GetLastError());
|
|
return(false);
|
|
}
|
|
if(result.retcode != TRADE_RETCODE_DONE)
|
|
{
|
|
Print("ModifyPositionSL retcode: ", result.retcode);
|
|
return(false);
|
|
}
|
|
|
|
Print("Modified SL for ticket #", ticket, " to ", DoubleToString(newSL,_Digits));
|
|
return(true);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Close position |
|
|
//+------------------------------------------------------------------+
|
|
bool ClosePosition(ulong ticket)
|
|
{
|
|
if(!PositionSelectByTicket(ticket))
|
|
return(false);
|
|
|
|
string sym = PositionGetString(POSITION_SYMBOL);
|
|
long type = PositionGetInteger(POSITION_TYPE);
|
|
double volume = PositionGetDouble(POSITION_VOLUME);
|
|
double price = 0.0;
|
|
|
|
MqlTick tick;
|
|
if(!SymbolInfoTick(sym, tick))
|
|
return(false);
|
|
|
|
if(type == POSITION_TYPE_BUY)
|
|
price = tick.bid;
|
|
else
|
|
price = tick.ask;
|
|
|
|
MqlTradeRequest request;
|
|
MqlTradeResult result;
|
|
ZeroMemory(request);
|
|
ZeroMemory(result);
|
|
|
|
request.action = TRADE_ACTION_DEAL;
|
|
request.magic = InpMagicNumber;
|
|
request.symbol = sym;
|
|
request.volume = volume;
|
|
request.type = (type == POSITION_TYPE_BUY ? ORDER_TYPE_SELL : ORDER_TYPE_BUY);
|
|
request.price = price;
|
|
request.deviation = 20;
|
|
request.type_filling= ORDER_FILLING_FOK;
|
|
request.comment = "Close";
|
|
|
|
if(!OrderSend(request, result))
|
|
{
|
|
Print("ClosePosition OrderSend failed. Error: ", GetLastError());
|
|
return(false);
|
|
}
|
|
if(result.retcode != TRADE_RETCODE_DONE)
|
|
{
|
|
Print("ClosePosition retcode: ", result.retcode);
|
|
return(false);
|
|
}
|
|
|
|
Print("Closed ticket #", ticket);
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|