This commit is contained in:
zhutoutoutousan
2026-05-02 15:55:04 +02:00
parent 0b4a70b843
commit b5acd37754
198 changed files with 18739 additions and 11364 deletions
+200 -19
View File
@@ -23,6 +23,14 @@ input double LotSize = 0.1; // Lot Size
input int MagicNumber = 123459123; // Magic Number
input int Slippage = 3; // Slippage in points
input group "=== Reversal escape (intrabar, multi-signal) ==="
input bool UseReversalEscape = true; // run while in position every tick
input int ReversalATRPeriod = 14; // ATR lookback on signal timeframe
input double ReversalAdverseAtrMult = 5.25; // close if price vs entry >= this * ATR
input int ReversalSignsRequired = 2; // how many independent signs must align
input double ReversalRsiVelocity = 16.0; // RSI points drop (long) / rise (short) vs prior buffer
input double ReversalBodyAtrMult = 5.1; // last closed bar body >= this * ATR counts as one sign
//--- Global variables
CTrade trade;
int rsi_handle;
@@ -35,6 +43,12 @@ datetime last_bar_time = 0;
bool rsi_against_position = false;
int bars_against_count = 0;
void ResetPositionTracking();
void SyncTrackedPosition();
double ATRPriceOnTF(const int period);
int CountReversalEscapeSigns(const ENUM_POSITION_TYPE ptype, const double atr);
void TryReversalEscape();
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
@@ -77,21 +91,38 @@ void OnTick()
{
return;
}
// Check if this is a new bar
datetime current_bar_time = iTime(_Symbol, TimeFrame, 0);
if(current_bar_time == last_bar_time)
bool is_new_bar = (current_bar_time != last_bar_time);
bool in_position = position_open || PositionExistsByMagic(_Symbol, MagicNumber);
// While flat, process only on new bars. While in position, allow intrabar reversal escape checks.
if(!in_position && !is_new_bar)
{
return; // Still the same bar, don't process
return;
}
last_bar_time = current_bar_time;
// Update RSI values
if(!UpdateRSI())
{
return;
}
if(in_position && UseReversalEscape)
{
TryReversalEscape();
}
if(!is_new_bar)
{
return;
}
last_bar_time = current_bar_time;
// Keep local tracking aligned with actual terminal positions for this symbol/magic.
SyncTrackedPosition();
// Check for existing position
CheckExistingPosition();
@@ -120,6 +151,155 @@ bool UpdateRSI()
return true;
}
//+------------------------------------------------------------------+
//| Wilder ATR in price units (signal timeframe) |
//+------------------------------------------------------------------+
double ATRPriceOnTF(const int period)
{
if(period < 1)
return 0.0;
MqlRates rates[];
const int need = period + 2;
if(CopyRates(_Symbol, TimeFrame, 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;
}
//+------------------------------------------------------------------+
//| Independent adverse signs (need ReversalSignsRequired to exit) |
//+------------------------------------------------------------------+
int CountReversalEscapeSigns(const ENUM_POSITION_TYPE ptype, const double atr)
{
if(atr <= 0.0)
return 0;
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
int signs = 0;
if(ptype == POSITION_TYPE_BUY)
{
if(entry - bid >= ReversalAdverseAtrMult * atr)
signs++;
if(rsi_prev - rsi_current >= ReversalRsiVelocity)
signs++;
}
else if(ptype == POSITION_TYPE_SELL)
{
if(ask - entry >= ReversalAdverseAtrMult * atr)
signs++;
if(rsi_current - rsi_prev >= ReversalRsiVelocity)
signs++;
}
else
{
return 0;
}
MqlRates rates[];
if(CopyRates(_Symbol, TimeFrame, 0, 4, rates) >= 4)
{
ArraySetAsSeries(rates, true);
const double body = MathAbs(rates[1].close - rates[1].open);
if(body >= ReversalBodyAtrMult * atr)
{
if(ptype == POSITION_TYPE_BUY && rates[1].close < rates[1].open)
signs++;
else if(ptype == POSITION_TYPE_SELL && rates[1].close > rates[1].open)
signs++;
}
if(ptype == POSITION_TYPE_BUY)
{
if(rates[1].close < rates[2].close && rates[2].close < rates[3].close)
signs++;
}
else
{
if(rates[1].close > rates[2].close && rates[2].close > rates[3].close)
signs++;
}
}
return signs;
}
//+------------------------------------------------------------------+
//| Cut losers fast on violent reversals (evaluated every tick) |
//+------------------------------------------------------------------+
void TryReversalEscape()
{
ulong live_ticket = GetPositionTicketByMagic(_Symbol, MagicNumber);
if(live_ticket == 0)
return;
if(!PositionSelectByTicketSymbolAndMagic(live_ticket, _Symbol, MagicNumber))
return;
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
const double atr = ATRPriceOnTF(ReversalATRPeriod);
if(atr <= 0.0)
return;
const int signs = CountReversalEscapeSigns(ptype, atr);
if(signs < ReversalSignsRequired)
return;
ClosePosition();
Print("RSIScalpingBTCUSD: reversal escape signs=", signs, " need=", ReversalSignsRequired,
" ATR=", DoubleToString(atr, (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS)));
}
//+------------------------------------------------------------------+
//| Reset local position tracking |
//+------------------------------------------------------------------+
void ResetPositionTracking()
{
position_open = false;
position_ticket = 0;
rsi_against_position = false;
bars_against_count = 0;
}
//+------------------------------------------------------------------+
//| Sync local state with real position in terminal |
//+------------------------------------------------------------------+
void SyncTrackedPosition()
{
ulong live_ticket = GetPositionTicketByMagic(_Symbol, MagicNumber);
if(live_ticket == 0)
{
ResetPositionTracking();
return;
}
// If we were not tracking (or ticket changed), start tracking the live position.
if(!position_open || position_ticket != (int)live_ticket)
{
if(PositionSelectByTicketSymbolAndMagic(live_ticket, _Symbol, MagicNumber))
{
position_open = true;
position_ticket = (int)live_ticket;
current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
rsi_against_position = false;
bars_against_count = 0;
}
return;
}
}
//+------------------------------------------------------------------+
//| Check existing position for exit conditions |
//+------------------------------------------------------------------+
@@ -133,10 +313,7 @@ void CheckExistingPosition()
// Check if position still exists with correct magic number AND symbol for THIS EA
if(!PositionSelectByTicketSymbolAndMagic(position_ticket, _Symbol, MagicNumber))
{
position_open = false;
position_ticket = 0;
rsi_against_position = false;
bars_against_count = 0;
ResetPositionTracking();
return;
}
@@ -308,20 +485,24 @@ void OpenSellPosition()
//+------------------------------------------------------------------+
void ClosePosition()
{
bool position_exists_before_close = PositionExistsByMagic(_Symbol, MagicNumber);
if(!position_exists_before_close)
{
ResetPositionTracking();
return;
}
// Close position using helper that verifies symbol AND magic number for THIS EA
if(ClosePositionByMagic(trade, _Symbol, MagicNumber))
{
position_open = false;
position_ticket = 0;
rsi_against_position = false;
bars_against_count = 0;
ResetPositionTracking();
}
else
{
// Position doesn't exist or wrong magic number - reset tracking
position_open = false;
position_ticket = 0;
rsi_against_position = false;
bars_against_count = 0;
// Keep tracking when close fails (e.g. market closed); retry on next bar.
if(!PositionExistsByMagic(_Symbol, MagicNumber))
{
ResetPositionTracking();
}
}
}
@@ -13,12 +13,15 @@ input double InpBreakBuffer = 110; // Break confirmation
input double InpLots = 0.10; // Position size
input long InpMagic = 26042501; // Magic number
input bool InpDrawTrendline = true; // Draw detected trendline
input bool InpUseSessionModeGate = true; // Block entries when symbol/session disallow opens
input bool InpBypassGateInTester = true; // Ignore gate in Strategy Tester for optimization
CTrade trade;
int g_maHandle = INVALID_HANDLE;
datetime g_lastBarTime = 0;
string g_lineName = "SimpleTrendline_Basis";
datetime g_lastEntryBlockLog = 0;
struct TrendlineModel
{
@@ -180,6 +183,75 @@ bool GetCurrentPosition(long &type, double &volume)
return true;
}
bool IsWithinAnyTradeSession(const datetime nowServer)
{
MqlDateTime dt;
TimeToStruct(nowServer, dt);
ENUM_DAY_OF_WEEK day = (ENUM_DAY_OF_WEEK)dt.day_of_week;
int nowSec = dt.hour * 3600 + dt.min * 60 + dt.sec;
datetime from = 0;
datetime to = 0;
bool hasAny = false;
for(uint idx = 0; idx < 16; idx++)
{
if(!SymbolInfoSessionTrade(_Symbol, day, idx, from, to))
break;
hasAny = true;
// SymbolInfoSessionTrade returns session boundaries as time-of-day values.
MqlDateTime fdt, tdt;
TimeToStruct(from, fdt);
TimeToStruct(to, tdt);
int fromSec = fdt.hour * 3600 + fdt.min * 60 + fdt.sec;
int toSec = tdt.hour * 3600 + tdt.min * 60 + tdt.sec;
// from==to on some brokers means full-day session.
if(fromSec == toSec)
{
return true;
}
else if(fromSec < toSec)
{
if(nowSec >= fromSec && nowSec <= toSec)
return true;
}
else
{
// Session passes midnight.
if(nowSec >= fromSec || nowSec <= toSec)
return true;
}
}
// If broker does not expose sessions for this symbol, do not block by session.
if(!hasAny)
return true;
return false;
}
bool CanOpenNewPositionNow(const ENUM_ORDER_TYPE orderType)
{
if(!InpUseSessionModeGate)
return true;
if(InpBypassGateInTester && (bool)MQLInfoInteger(MQL_TESTER))
return true;
long tradeMode = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE);
if(tradeMode == SYMBOL_TRADE_MODE_DISABLED ||
tradeMode == SYMBOL_TRADE_MODE_CLOSEONLY)
return false;
if(orderType == ORDER_TYPE_BUY &&
tradeMode == SYMBOL_TRADE_MODE_SHORTONLY)
return false;
if(orderType == ORDER_TYPE_SELL &&
tradeMode == SYMBOL_TRADE_MODE_LONGONLY)
return false;
if(!IsWithinAnyTradeSession(TimeCurrent()))
return false;
return true;
}
void TryExitOnBreak(const TrendlineModel &m)
{
long posType;
@@ -234,6 +306,16 @@ void TryPullbackEntry(const TrendlineModel &m)
bool stillHealthy = (b2.close >= TrendlinePriceAtTime(m, b2.time) - tol);
if(touched && reclaim && bullish && stillHealthy)
{
if(!CanOpenNewPositionNow(ORDER_TYPE_BUY))
{
datetime nowBar = iTime(_Symbol, _Period, 0);
if(nowBar != g_lastEntryBlockLog)
{
g_lastEntryBlockLog = nowBar;
Print("Buy entry skipped: symbol mode/session does not allow opening now");
}
return;
}
trade.Buy(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback buy");
}
}
@@ -245,6 +327,16 @@ void TryPullbackEntry(const TrendlineModel &m)
bool stillWeak = (b2.close <= TrendlinePriceAtTime(m, b2.time) + tol);
if(touched && reject && bearish && stillWeak)
{
if(!CanOpenNewPositionNow(ORDER_TYPE_SELL))
{
datetime nowBar = iTime(_Symbol, _Period, 0);
if(nowBar != g_lastEntryBlockLog)
{
g_lastEntryBlockLog = nowBar;
Print("Sell entry skipped: symbol mode/session does not allow opening now");
}
return;
}
trade.Sell(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback sell");
}
}