Files
zhutoutoutousanandCursor 605faf5310 Prepare source-only public release for develop.
Add cluster audit pipeline, united EA updates, brochure generators, and publication hygiene (gitignore, MT5 path desensitization, pre-upload scan). Remove tracked reports, models, and binary artifacts from the repo.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 15:03:43 +02:00

220 lines
6.1 KiB
Plaintext

//+------------------------------------------------------------------+
//| XAUBearTrendStrategy.mqh — XAUUSD bear-regime rally-fade shorts |
//+------------------------------------------------------------------+
#ifndef XAU_BEAR_TREND_STRATEGY_MQH
#define XAU_BEAR_TREND_STRATEGY_MQH
struct XAUBearTrendData
{
string symbol;
bool isInitialized;
CTrade trade;
ENUM_TIMEFRAMES regimeTF;
ENUM_TIMEFRAMES entryTF;
int regimeEmaPeriod;
int rsiPeriod;
double rsiArmLevel;
double rsiTriggerLevel;
int atrPeriod;
double slAtrMult;
double tpAtrMult;
bool useTrailing;
double trailAtrMult;
ulong magic;
int slippage;
int maxSpreadPoints;
bool closeUnprofitableOnNewSignal;
int h_regimeEma;
int h_rsi;
int h_atr;
datetime lastBarTime;
bool rsiArmed;
};
bool XBT_Copy1(const int handle, double &v)
{
double b[];
ArraySetAsSeries(b, true);
if(CopyBuffer(handle, 0, 0, 1, b) < 1)
return false;
v = b[0];
return true;
}
bool XBT_RegimeBearish(XAUBearTrendData &d)
{
double ema = 0.0;
if(!XBT_Copy1(d.h_regimeEma, ema))
return false;
const double close1 = iClose(d.symbol, d.regimeTF, 1);
return (close1 > 0.0 && ema > 0.0 && close1 < ema);
}
bool XBT_IsNewEntryBar(XAUBearTrendData &d)
{
datetime t = iTime(d.symbol, d.entryTF, 0);
if(t <= 0 || t == d.lastBarTime)
return false;
d.lastBarTime = t;
return true;
}
bool InitXAUBearTrend(XAUBearTrendData &d,
const string symbol,
const ENUM_TIMEFRAMES regimeTF,
const ENUM_TIMEFRAMES entryTF,
const int regimeEmaPeriod,
const int rsiPeriod,
const double rsiArmLevel,
const double rsiTriggerLevel,
const int atrPeriod,
const double slAtrMult,
const double tpAtrMult,
const bool useTrailing,
const double trailAtrMult,
const ulong magic,
const int slippage,
const int maxSpreadPoints)
{
d.symbol = symbol;
d.regimeTF = regimeTF;
d.entryTF = entryTF;
d.regimeEmaPeriod = regimeEmaPeriod;
d.rsiPeriod = rsiPeriod;
d.rsiArmLevel = rsiArmLevel;
d.rsiTriggerLevel = rsiTriggerLevel;
d.atrPeriod = atrPeriod;
d.slAtrMult = slAtrMult;
d.tpAtrMult = tpAtrMult;
d.useTrailing = useTrailing;
d.trailAtrMult = trailAtrMult;
d.magic = magic;
d.slippage = slippage;
d.maxSpreadPoints = maxSpreadPoints;
d.lastBarTime = 0;
d.rsiArmed = false;
if(!SymbolSelect(symbol, true))
{
Print("XAUBearTrend: symbol not available: ", symbol);
d.isInitialized = false;
return false;
}
d.h_regimeEma = iMA(symbol, regimeTF, regimeEmaPeriod, 0, MODE_EMA, PRICE_CLOSE);
d.h_rsi = iRSI(symbol, entryTF, rsiPeriod, PRICE_CLOSE);
d.h_atr = iATR(symbol, entryTF, atrPeriod);
if(d.h_regimeEma == INVALID_HANDLE || d.h_rsi == INVALID_HANDLE || d.h_atr == INVALID_HANDLE)
{
Print("XAUBearTrend: indicator init failed for ", symbol);
d.isInitialized = false;
return false;
}
d.trade.SetExpertMagicNumber((long)magic);
d.trade.SetDeviationInPoints(slippage);
d.trade.SetTypeFillingBySymbol(symbol);
d.isInitialized = true;
return true;
}
void DeinitXAUBearTrend(XAUBearTrendData &d)
{
if(d.h_regimeEma != INVALID_HANDLE) IndicatorRelease(d.h_regimeEma);
if(d.h_rsi != INVALID_HANDLE) IndicatorRelease(d.h_rsi);
if(d.h_atr != INVALID_HANDLE) IndicatorRelease(d.h_atr);
d.isInitialized = false;
}
void XBT_ManageShort(XAUBearTrendData &d)
{
if(!PositionSelectByMagic(d.symbol, d.magic))
return;
if((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_SELL)
return;
if(!XBT_RegimeBearish(d))
{
d.trade.PositionClose(PositionGetInteger(POSITION_TICKET));
d.rsiArmed = false;
return;
}
double atr = 0.0;
if(!XBT_Copy1(d.h_atr, atr) || atr <= 0.0)
return;
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
const double bid = SymbolInfoDouble(d.symbol, SYMBOL_BID);
double sl = PositionGetDouble(POSITION_SL);
double tp = PositionGetDouble(POSITION_TP);
if(d.useTrailing)
{
const double trail = bid + atr * d.trailAtrMult;
if(sl <= 0.0 || trail < sl)
sl = trail;
}
if(tp <= 0.0 && d.tpAtrMult > 0.0)
tp = entry - atr * d.tpAtrMult;
if(sl > 0.0 || tp > 0.0)
d.trade.PositionModify(PositionGetInteger(POSITION_TICKET), sl, tp);
}
void ProcessXAUBearTrend(XAUBearTrendData &d, const double lotSize)
{
if(!d.isInitialized || lotSize <= 0.0)
return;
const long spread = SymbolInfoInteger(d.symbol, SYMBOL_SPREAD);
if(spread > d.maxSpreadPoints)
return;
XBT_ManageShort(d);
if(!XBT_IsNewEntryBar(d))
return;
if(!XBT_RegimeBearish(d))
{
d.rsiArmed = false;
return;
}
double rsi[];
ArraySetAsSeries(rsi, true);
if(CopyBuffer(d.h_rsi, 0, 1, 2, rsi) < 2)
return;
if(rsi[0] >= d.rsiArmLevel)
d.rsiArmed = true;
if(!d.rsiArmed || rsi[0] >= d.rsiTriggerLevel)
return;
if(PositionExistsByMagic(d.symbol, d.magic))
return;
if(!United_MayOpenNewEntry(d.symbol, d.magic, false, d.trade, d.closeUnprofitableOnNewSignal))
return;
double atr = 0.0;
if(!XBT_Copy1(d.h_atr, atr) || atr <= 0.0)
return;
const double barHigh = iHigh(d.symbol, d.entryTF, 1);
const double ask = SymbolInfoDouble(d.symbol, SYMBOL_ASK);
const double sl = barHigh + atr * d.slAtrMult;
const double tp = (d.tpAtrMult > 0.0 ? ask - atr * d.tpAtrMult : 0.0);
const double lots = United_NormalizeVolume(d.symbol, lotSize);
if(d.trade.Sell(lots, d.symbol, 0.0, sl, tp, "XBT rally fade"))
d.rsiArmed = false;
}
#endif