616 lines
20 KiB
Plaintext
616 lines
20 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| RSIScalpingStrategy.mqh |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| RSI Scalping Strategy Data Structure |
|
|
//+------------------------------------------------------------------+
|
|
struct RSIScalpingData {
|
|
string symbol;
|
|
bool isInitialized;
|
|
CTrade trade;
|
|
int rsi_handle;
|
|
double rsi_buffer[];
|
|
double rsi_prev;
|
|
double rsi_current;
|
|
double rsi_two_bars_ago;
|
|
bool position_open;
|
|
ulong position_ticket;
|
|
ENUM_POSITION_TYPE current_position_type;
|
|
datetime last_bar_time;
|
|
bool rsi_against_position;
|
|
int bars_against_count;
|
|
};
|
|
|
|
void ClosePosition(RSIScalpingData& data, int MagicNumber);
|
|
|
|
double RS_ATRPriceOnTF(const string symbol, const ENUM_TIMEFRAMES tf, const int period)
|
|
{
|
|
if(period < 1)
|
|
return 0.0;
|
|
MqlRates rates[];
|
|
const int need = period + 2;
|
|
if(CopyRates(symbol, tf, 0, need, rates) < need)
|
|
return 0.0;
|
|
ArraySetAsSeries(rates, true);
|
|
double sum = 0.0;
|
|
for(int i = 1; i <= period; i++)
|
|
{
|
|
const double hl = rates[i].high - rates[i].low;
|
|
const double hc = MathAbs(rates[i].high - rates[i + 1].close);
|
|
const double lc = MathAbs(rates[i].low - rates[i + 1].close);
|
|
sum += MathMax(hl, MathMax(hc, lc));
|
|
}
|
|
return sum / (double)period;
|
|
}
|
|
|
|
int RS_CountReversalEscapeSigns(RSIScalpingData& data, const ENUM_TIMEFRAMES tf,
|
|
const ENUM_POSITION_TYPE ptype, const double atr,
|
|
const double adverseAtrMult, const double rsiVelocity,
|
|
const double bodyAtrMult)
|
|
{
|
|
if(atr <= 0.0)
|
|
return 0;
|
|
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
const double bid = SymbolInfoDouble(data.symbol, SYMBOL_BID);
|
|
const double ask = SymbolInfoDouble(data.symbol, SYMBOL_ASK);
|
|
int signs = 0;
|
|
|
|
if(ptype == POSITION_TYPE_BUY)
|
|
{
|
|
if(entry - bid >= adverseAtrMult * atr)
|
|
signs++;
|
|
if(data.rsi_prev - data.rsi_current >= rsiVelocity)
|
|
signs++;
|
|
}
|
|
else if(ptype == POSITION_TYPE_SELL)
|
|
{
|
|
if(ask - entry >= adverseAtrMult * atr)
|
|
signs++;
|
|
if(data.rsi_current - data.rsi_prev >= rsiVelocity)
|
|
signs++;
|
|
}
|
|
else
|
|
return 0;
|
|
|
|
MqlRates r[];
|
|
if(CopyRates(data.symbol, tf, 0, 4, r) >= 4)
|
|
{
|
|
ArraySetAsSeries(r, true);
|
|
const double body = MathAbs(r[1].close - r[1].open);
|
|
if(body >= bodyAtrMult * atr)
|
|
{
|
|
if(ptype == POSITION_TYPE_BUY && r[1].close < r[1].open)
|
|
signs++;
|
|
else if(ptype == POSITION_TYPE_SELL && r[1].close > r[1].open)
|
|
signs++;
|
|
}
|
|
if(ptype == POSITION_TYPE_BUY)
|
|
{
|
|
if(r[1].close < r[2].close && r[2].close < r[3].close)
|
|
signs++;
|
|
}
|
|
else
|
|
{
|
|
if(r[1].close > r[2].close && r[2].close > r[3].close)
|
|
signs++;
|
|
}
|
|
}
|
|
return signs;
|
|
}
|
|
|
|
void RS_TryReversalEscape(RSIScalpingData& data, const ENUM_TIMEFRAMES tf, const int MagicNumber,
|
|
const int atrPeriod, const double adverseAtrMult, const int signsRequired,
|
|
const double rsiVelocity, const double bodyAtrMult)
|
|
{
|
|
if(!PositionSelectByMagic(data.symbol, (ulong)MagicNumber))
|
|
return;
|
|
|
|
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
const double atr = RS_ATRPriceOnTF(data.symbol, tf, atrPeriod);
|
|
if(atr <= 0.0)
|
|
return;
|
|
|
|
const int n = RS_CountReversalEscapeSigns(data, tf, ptype, atr, adverseAtrMult, rsiVelocity, bodyAtrMult);
|
|
if(n < signsRequired)
|
|
return;
|
|
|
|
ClosePosition(data, MagicNumber);
|
|
Print("RSIScalping: reversal escape symbol=", data.symbol, " signs=", n, " need=", signsRequired,
|
|
" ATR=", DoubleToString(atr, (int)SymbolInfoInteger(data.symbol, SYMBOL_DIGITS)));
|
|
}
|
|
|
|
void RS_ApplyTrailingStop(RSIScalpingData& data, const int MagicNumber,
|
|
const bool useTrailingStop,
|
|
const double trailingStopDistancePoints,
|
|
const double trailingActivationPoints)
|
|
{
|
|
if(!useTrailingStop || trailingStopDistancePoints <= 0.0)
|
|
return;
|
|
if(!PositionSelectByMagic(data.symbol, (ulong)MagicNumber))
|
|
return;
|
|
|
|
const double point = SymbolInfoDouble(data.symbol, SYMBOL_POINT);
|
|
if(point <= 0.0)
|
|
return;
|
|
|
|
const int digits = (int)SymbolInfoInteger(data.symbol, SYMBOL_DIGITS);
|
|
const double trail_dist = trailingStopDistancePoints * point;
|
|
const double activation_pts = (trailingActivationPoints > 0.0)
|
|
? trailingActivationPoints
|
|
: trailingStopDistancePoints;
|
|
const double activation = activation_pts * point;
|
|
const long stops_level = SymbolInfoInteger(data.symbol, SYMBOL_TRADE_STOPS_LEVEL);
|
|
const double min_dist = (double)stops_level * point;
|
|
|
|
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
const double cur_sl = PositionGetDouble(POSITION_SL);
|
|
const double cur_tp = PositionGetDouble(POSITION_TP);
|
|
|
|
if(ptype == POSITION_TYPE_BUY)
|
|
{
|
|
const double bid = SymbolInfoDouble(data.symbol, SYMBOL_BID);
|
|
if(bid - entry <= activation)
|
|
return;
|
|
|
|
double new_sl = NormalizeDouble(bid - trail_dist, digits);
|
|
if(min_dist > 0.0 && bid - new_sl < min_dist)
|
|
new_sl = NormalizeDouble(bid - min_dist, digits);
|
|
|
|
if(new_sl >= bid || new_sl <= 0.0)
|
|
return;
|
|
if(cur_sl > 0.0 && new_sl <= cur_sl)
|
|
return;
|
|
|
|
ModifyPositionByMagic(data.trade, data.symbol, (ulong)MagicNumber, new_sl, cur_tp);
|
|
}
|
|
else if(ptype == POSITION_TYPE_SELL)
|
|
{
|
|
const double ask = SymbolInfoDouble(data.symbol, SYMBOL_ASK);
|
|
if(entry - ask <= activation)
|
|
return;
|
|
|
|
double new_sl = NormalizeDouble(ask + trail_dist, digits);
|
|
if(min_dist > 0.0 && new_sl - ask < min_dist)
|
|
new_sl = NormalizeDouble(ask + min_dist, digits);
|
|
|
|
if(new_sl <= ask || new_sl <= 0.0)
|
|
return;
|
|
if(cur_sl > 0.0 && new_sl >= cur_sl)
|
|
return;
|
|
|
|
ModifyPositionByMagic(data.trade, data.symbol, (ulong)MagicNumber, new_sl, cur_tp);
|
|
}
|
|
}
|
|
|
|
string ErrorDescription(int errorCode)
|
|
{
|
|
switch(errorCode)
|
|
{
|
|
case 4801: return "Symbol not found";
|
|
case 4802: return "Symbol not selected";
|
|
case 4803: return "Symbol not visible";
|
|
case 4804: return "Symbol not available";
|
|
case 4805: return "Cannot load indicator - insufficient history data";
|
|
default: return "Unknown error " + IntegerToString(errorCode);
|
|
}
|
|
}
|
|
|
|
bool InitRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES TimeFrame, int RSI_Period,
|
|
ENUM_APPLIED_PRICE RSI_Applied_Price, int MagicNumber, int Slippage)
|
|
{
|
|
data.symbol = symbol;
|
|
data.isInitialized = false;
|
|
|
|
// Check if symbol exists
|
|
if(!SymbolSelect(symbol, true))
|
|
{
|
|
Print("RSIScalping: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
|
|
return false; // Return false but don't fail entire EA
|
|
}
|
|
|
|
// Wait a bit for symbol to be ready
|
|
Sleep(100);
|
|
|
|
// Try to create RSI indicator with retry logic (for insufficient history in backtesting)
|
|
data.rsi_handle = INVALID_HANDLE;
|
|
int retryCount = 0;
|
|
int maxRetries = 5;
|
|
|
|
while(retryCount < maxRetries && data.rsi_handle == INVALID_HANDLE)
|
|
{
|
|
data.rsi_handle = iRSI(symbol, TimeFrame, RSI_Period, RSI_Applied_Price);
|
|
|
|
if(data.rsi_handle == INVALID_HANDLE)
|
|
{
|
|
int error = GetLastError();
|
|
|
|
// Error 4805 = insufficient history - wait longer and retry
|
|
if(error == 4805 && retryCount < maxRetries - 1)
|
|
{
|
|
Sleep(1000); // Wait 1 second for history to load
|
|
retryCount++;
|
|
continue;
|
|
}
|
|
|
|
Print("RSIScalping: Error creating RSI indicator for '", symbol, "' - Error: ", error, " (", ErrorDescription(error), ")");
|
|
return false; // Return false but don't fail entire EA
|
|
}
|
|
}
|
|
|
|
if(data.rsi_handle == INVALID_HANDLE)
|
|
{
|
|
Print("RSIScalping: Failed to create RSI indicator for '", symbol, "' after ", maxRetries, " retries");
|
|
return false;
|
|
}
|
|
|
|
data.trade.SetExpertMagicNumber(MagicNumber);
|
|
data.trade.SetDeviationInPoints(Slippage);
|
|
data.trade.SetTypeFilling(ORDER_FILLING_FOK);
|
|
|
|
ArraySetAsSeries(data.rsi_buffer, true);
|
|
data.position_open = false;
|
|
data.position_ticket = 0;
|
|
data.rsi_against_position = false;
|
|
data.bars_against_count = 0;
|
|
data.isInitialized = true;
|
|
|
|
Print("RSIScalping: Successfully initialized for symbol '", symbol, "'");
|
|
return true;
|
|
}
|
|
|
|
void DeinitRSIScalping(RSIScalpingData& data)
|
|
{
|
|
if(data.rsi_handle != INVALID_HANDLE)
|
|
IndicatorRelease(data.rsi_handle);
|
|
}
|
|
|
|
bool UpdateRSI(RSIScalpingData& data)
|
|
{
|
|
if(CopyBuffer(data.rsi_handle, 0, 0, 3, data.rsi_buffer) < 3)
|
|
return false;
|
|
|
|
data.rsi_current = data.rsi_buffer[0];
|
|
data.rsi_prev = data.rsi_buffer[1];
|
|
data.rsi_two_bars_ago = data.rsi_buffer[2];
|
|
|
|
return true;
|
|
}
|
|
|
|
void CheckExistingPosition(RSIScalpingData& data, ENUM_TIMEFRAMES TimeFrame, int MagicNumber,
|
|
double RSI_Oversold, double RSI_Overbought, double RSI_Target_Buy,
|
|
double RSI_Target_Sell, int BarsToWait)
|
|
{
|
|
// Always check if position exists, even if tracking says it doesn't
|
|
bool positionExists = PositionExistsByMagic(data.symbol, MagicNumber);
|
|
|
|
if(!positionExists && data.position_open)
|
|
{
|
|
// Position was closed externally, reset tracking
|
|
data.position_open = false;
|
|
data.position_ticket = 0;
|
|
data.rsi_against_position = false;
|
|
data.bars_against_count = 0;
|
|
return;
|
|
}
|
|
|
|
if(!positionExists)
|
|
return;
|
|
|
|
// Update tracking if we have a position but tracking was lost
|
|
if(!data.position_open && positionExists)
|
|
{
|
|
ulong ticket = GetPositionTicketByMagic(data.symbol, MagicNumber);
|
|
if(ticket > 0 && PositionSelectByTicketSymbolAndMagic(ticket, data.symbol, MagicNumber))
|
|
{
|
|
data.position_ticket = ticket;
|
|
data.position_open = true;
|
|
data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
}
|
|
}
|
|
|
|
// Verify our tracked position still exists
|
|
if(data.position_open && data.position_ticket > 0)
|
|
{
|
|
if(!PositionSelectByTicketSymbolAndMagic(data.position_ticket, data.symbol, MagicNumber))
|
|
{
|
|
// Try to find the position again
|
|
ulong ticket = GetPositionTicketByMagic(data.symbol, MagicNumber);
|
|
if(ticket > 0 && PositionSelectByTicketSymbolAndMagic(ticket, data.symbol, MagicNumber))
|
|
{
|
|
data.position_ticket = ticket;
|
|
data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
}
|
|
else
|
|
{
|
|
// Position doesn't exist, reset tracking
|
|
data.position_open = false;
|
|
data.position_ticket = 0;
|
|
data.rsi_against_position = false;
|
|
data.bars_against_count = 0;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Update position type in case it changed (shouldn't happen, but be safe)
|
|
data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
}
|
|
}
|
|
|
|
if(data.current_position_type == POSITION_TYPE_BUY)
|
|
{
|
|
if(data.rsi_current < RSI_Oversold)
|
|
{
|
|
if(!data.rsi_against_position)
|
|
{
|
|
data.rsi_against_position = true;
|
|
data.bars_against_count = 1;
|
|
}
|
|
else
|
|
{
|
|
data.bars_against_count++;
|
|
}
|
|
|
|
if(data.bars_against_count >= BarsToWait)
|
|
{
|
|
ClosePosition(data, MagicNumber);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(data.rsi_against_position)
|
|
{
|
|
data.rsi_against_position = false;
|
|
data.bars_against_count = 0;
|
|
}
|
|
|
|
if(data.rsi_current >= RSI_Target_Buy)
|
|
{
|
|
ClosePosition(data, MagicNumber);
|
|
}
|
|
}
|
|
}
|
|
else if(data.current_position_type == POSITION_TYPE_SELL)
|
|
{
|
|
if(data.rsi_current > RSI_Overbought)
|
|
{
|
|
if(!data.rsi_against_position)
|
|
{
|
|
data.rsi_against_position = true;
|
|
data.bars_against_count = 1;
|
|
}
|
|
else
|
|
{
|
|
data.bars_against_count++;
|
|
}
|
|
|
|
if(data.bars_against_count >= BarsToWait)
|
|
{
|
|
ClosePosition(data, MagicNumber);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(data.rsi_against_position)
|
|
{
|
|
data.rsi_against_position = false;
|
|
data.bars_against_count = 0;
|
|
}
|
|
|
|
if(data.rsi_current <= RSI_Target_Sell)
|
|
{
|
|
ClosePosition(data, MagicNumber);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CheckEntrySignals(RSIScalpingData& data, ENUM_TIMEFRAMES TimeFrame, int MagicNumber,
|
|
double RSI_Oversold, double RSI_Overbought, double LotSize)
|
|
{
|
|
if(data.rsi_two_bars_ago <= RSI_Oversold && data.rsi_prev > RSI_Oversold)
|
|
{
|
|
OpenBuyPosition(data, MagicNumber, LotSize);
|
|
}
|
|
|
|
if(data.rsi_two_bars_ago >= RSI_Overbought && data.rsi_prev < RSI_Overbought)
|
|
{
|
|
OpenSellPosition(data, MagicNumber, LotSize);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Normalize Lot Size According to Symbol Properties |
|
|
//+------------------------------------------------------------------+
|
|
double NormalizeLotSize(string symbol, double lotSize)
|
|
{
|
|
return United_NormalizeVolume(symbol, lotSize);
|
|
}
|
|
|
|
void OpenBuyPosition(RSIScalpingData& data, int MagicNumber, double LotSize)
|
|
{
|
|
if(PositionExistsByMagic(data.symbol, MagicNumber))
|
|
return;
|
|
|
|
// Normalize lot size according to symbol properties
|
|
double normalizedLot = NormalizeLotSize(data.symbol, LotSize);
|
|
|
|
double ask = SymbolInfoDouble(data.symbol, SYMBOL_ASK);
|
|
|
|
if(data.trade.Buy(normalizedLot, data.symbol, ask, 0, 0, "RSI Scalping Buy"))
|
|
{
|
|
ulong new_ticket = data.trade.ResultOrder();
|
|
if(new_ticket > 0)
|
|
{
|
|
if(PositionSelectByTicketSymbolAndMagic(new_ticket, data.symbol, MagicNumber))
|
|
{
|
|
data.position_ticket = new_ticket;
|
|
data.position_open = true;
|
|
data.current_position_type = POSITION_TYPE_BUY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void OpenSellPosition(RSIScalpingData& data, int MagicNumber, double LotSize)
|
|
{
|
|
if(PositionExistsByMagic(data.symbol, MagicNumber))
|
|
return;
|
|
|
|
// Normalize lot size according to symbol properties
|
|
double normalizedLot = NormalizeLotSize(data.symbol, LotSize);
|
|
|
|
double bid = SymbolInfoDouble(data.symbol, SYMBOL_BID);
|
|
|
|
if(data.trade.Sell(normalizedLot, data.symbol, bid, 0, 0, "RSI Scalping Sell"))
|
|
{
|
|
ulong new_ticket = data.trade.ResultOrder();
|
|
if(new_ticket > 0)
|
|
{
|
|
if(PositionSelectByTicketSymbolAndMagic(new_ticket, data.symbol, MagicNumber))
|
|
{
|
|
data.position_ticket = new_ticket;
|
|
data.position_open = true;
|
|
data.current_position_type = POSITION_TYPE_SELL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClosePosition(RSIScalpingData& data, int MagicNumber)
|
|
{
|
|
// First verify position still exists
|
|
if(!PositionExistsByMagic(data.symbol, MagicNumber))
|
|
{
|
|
// Position doesn't exist, reset tracking
|
|
data.position_open = false;
|
|
data.position_ticket = 0;
|
|
data.rsi_against_position = false;
|
|
data.bars_against_count = 0;
|
|
return;
|
|
}
|
|
|
|
// Try to close by ticket first (more reliable)
|
|
bool closed = false;
|
|
if(data.position_ticket > 0)
|
|
{
|
|
if(PositionSelectByTicket(data.position_ticket))
|
|
{
|
|
// Verify it's our position
|
|
if(PositionGetString(POSITION_SYMBOL) == data.symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == MagicNumber)
|
|
{
|
|
closed = data.trade.PositionClose(data.position_ticket);
|
|
if(!closed)
|
|
{
|
|
Print("RSIScalping: Failed to close position by ticket ", data.position_ticket,
|
|
" - Error: ", data.trade.ResultRetcode(), " (", data.trade.ResultRetcodeDescription(), ")");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If ticket method failed, try magic number method
|
|
if(!closed)
|
|
{
|
|
closed = ClosePositionByMagic(data.trade, data.symbol, MagicNumber);
|
|
if(!closed)
|
|
{
|
|
Print("RSIScalping: Failed to close position by magic number for '", data.symbol,
|
|
"' - Error: ", data.trade.ResultRetcode(), " (", data.trade.ResultRetcodeDescription(), ")");
|
|
}
|
|
}
|
|
|
|
// Verify position is actually closed
|
|
if(closed)
|
|
{
|
|
// Wait a moment and verify
|
|
Sleep(50);
|
|
if(!PositionExistsByMagic(data.symbol, MagicNumber))
|
|
{
|
|
data.position_open = false;
|
|
data.position_ticket = 0;
|
|
data.rsi_against_position = false;
|
|
data.bars_against_count = 0;
|
|
Print("RSIScalping: Position successfully closed for '", data.symbol, "'");
|
|
}
|
|
else
|
|
{
|
|
Print("RSIScalping: Warning - Close returned success but position still exists for '", data.symbol, "'");
|
|
// Try one more time
|
|
Sleep(100);
|
|
if(PositionExistsByMagic(data.symbol, MagicNumber))
|
|
{
|
|
ClosePositionByMagic(data.trade, data.symbol, MagicNumber);
|
|
}
|
|
// Reset tracking anyway to prevent getting stuck
|
|
data.position_open = false;
|
|
data.position_ticket = 0;
|
|
data.rsi_against_position = false;
|
|
data.bars_against_count = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Close failed, but reset tracking to prevent getting stuck
|
|
// The position might have been closed externally
|
|
data.position_open = false;
|
|
data.position_ticket = 0;
|
|
data.rsi_against_position = false;
|
|
data.bars_against_count = 0;
|
|
}
|
|
}
|
|
|
|
void ProcessRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES TimeFrame, int RSI_Period,
|
|
ENUM_APPLIED_PRICE RSI_Applied_Price, double RSI_Overbought,
|
|
double RSI_Oversold, double RSI_Target_Buy, double RSI_Target_Sell,
|
|
int BarsToWait, double LotSize, int MagicNumber,
|
|
bool UseReversalEscape, int ReversalATRPeriod, double ReversalAdverseAtrMult,
|
|
int ReversalSignsRequired, double ReversalRsiVelocity, double ReversalBodyAtrMult,
|
|
bool UseTrailingStop, double TrailingStopDistancePoints, double TrailingActivationPoints)
|
|
{
|
|
// Skip if not initialized (symbol not available)
|
|
if(!data.isInitialized)
|
|
return;
|
|
|
|
data.symbol = symbol; // Update symbol in case it changed
|
|
if(Bars(data.symbol, TimeFrame) < RSI_Period + 2)
|
|
return;
|
|
|
|
const datetime current_bar_time = iTime(data.symbol, TimeFrame, 0);
|
|
const bool new_bar = (current_bar_time != data.last_bar_time);
|
|
const bool in_pos = data.position_open || PositionExistsByMagic(data.symbol, MagicNumber);
|
|
if(!in_pos && !new_bar)
|
|
return;
|
|
|
|
if(!UpdateRSI(data))
|
|
return;
|
|
|
|
if(in_pos && UseReversalEscape)
|
|
RS_TryReversalEscape(data, TimeFrame, MagicNumber, ReversalATRPeriod, ReversalAdverseAtrMult,
|
|
ReversalSignsRequired, ReversalRsiVelocity, ReversalBodyAtrMult);
|
|
|
|
if(in_pos)
|
|
RS_ApplyTrailingStop(data, MagicNumber, UseTrailingStop,
|
|
TrailingStopDistancePoints, TrailingActivationPoints);
|
|
|
|
if(!new_bar)
|
|
return;
|
|
|
|
data.last_bar_time = current_bar_time;
|
|
|
|
CheckExistingPosition(data, TimeFrame, MagicNumber, RSI_Oversold, RSI_Overbought,
|
|
RSI_Target_Buy, RSI_Target_Sell, BarsToWait);
|
|
|
|
if(!data.position_open && !PositionExistsByMagic(data.symbol, MagicNumber))
|
|
{
|
|
CheckEntrySignals(data, TimeFrame, MagicNumber, RSI_Oversold, RSI_Overbought, LotSize);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|