//+------------------------------------------------------------------+
//| DBD_RBR.mq5 |
//| Copyright 2024, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
#property version "1.01"
#property description "DBD/RBR Pattern Trading Strategy"
input int SwingPeriod = 5; // Swing detection period
input double MinSwingPct = 0.1; // Min swing size (% of price)
input int TradeDistance = 50; // Min distance from swing (points)
input double RiskPercent = 1.0; // Risk per trade (%)
input double RewardRatio = 2.0; // Reward/Risk ratio
// Global variables
double swingHighs[], swingLows[];
datetime lastTradeTime = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
ArraySetAsSeries(swingHighs, true);
ArraySetAsSeries(swingLows, true);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
if(!NewBar()) return;
UpdateSwingPoints();
CheckPatterns();
}
//+------------------------------------------------------------------+
//| Detect new bar formation |
//+------------------------------------------------------------------+
bool NewBar()
{
static datetime prevTime;
if(prevTime != iTime(_Symbol, _Period, 0)) {
prevTime = iTime(_Symbol, _Period, 0);
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Update swing high/low points |
//+------------------------------------------------------------------+
void UpdateSwingPoints()
{
int bars = Bars(_Symbol, _Period);
if(bars < SwingPeriod*3) return;
ArrayResize(swingHighs, bars);
ArrayResize(swingLows, bars);
ArrayInitialize(swingHighs, 0.0);
ArrayInitialize(swingLows, 0.0);
// Find swing points
for(int i = SwingPeriod; i < bars - SwingPeriod; i++) {
if(IsSwingHigh(i)) {
swingHighs[i] = iHigh(_Symbol, _Period, i);
}
if(IsSwingLow(i)) {
swingLows[i] = iLow(_Symbol, _Period, i);
}
}
}
//+------------------------------------------------------------------+
//| Check if bar is a swing high |
//+------------------------------------------------------------------+
bool IsSwingHigh(int index)
{
for(int i = 1; i <= SwingPeriod; i++) {
if(iHigh(_Symbol, _Period, index) < iHigh(_Symbol, _Period, index-i) ||
iHigh(_Symbol, _Period, index) < iHigh(_Symbol, _Period, index+i))
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Check if bar is a swing low |
//+------------------------------------------------------------------+
bool IsSwingLow(int index)
{
for(int i = 1; i <= SwingPeriod; i++) {
if(iLow(_Symbol, _Period, index) > iLow(_Symbol, _Period, index-i) ||
iLow(_Symbol, _Period, index) > iLow(_Symbol, _Period, index+i))
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Check for DBD/RBR patterns |
//+------------------------------------------------------------------+
void CheckPatterns()
{
int bar = 1;
double minSwing = SymbolInfoDouble(_Symbol, SYMBOL_ASK) * MinSwingPct / 100;
double minDistance = TradeDistance * SymbolInfoDouble(_Symbol, SYMBOL_POINT);
// Find recent swing points
int highIdx = -1, lowIdx = -1;
for(int i = bar; i < ArraySize(swingHighs); i++) {
if(swingHighs[i] > 0) highIdx = i;
if(swingLows[i] > 0) lowIdx = i;
if(highIdx > 0 && lowIdx > 0) break;
}
if(highIdx < 0 || lowIdx < 0) return;
// Check Rally Base Rally (RBR) pattern
if(highIdx > lowIdx) {
double baseLow = swingLows[lowIdx];
double rallyHigh = swingHighs[highIdx];
double rallySize = rallyHigh - baseLow;
if(rallySize > minSwing) {
double currentClose = iClose(_Symbol, _Period, bar);
if(currentClose > rallyHigh + minDistance) {
double entry = NormalizeDouble(rallyHigh + minDistance, _Digits);
double sl = NormalizeDouble(baseLow, _Digits);
double tp = NormalizeDouble(entry + (rallySize * RewardRatio),
_Digits);
// Calculate stop level in price terms
double stopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL)
* SymbolInfoDouble(_Symbol, SYMBOL_POINT);
if(entry - sl > stopLevel) {
OpenTrade(ORDER_TYPE_BUY, entry, sl, tp);
}
}
}
}
// Check Drop Base Drop (DBD) pattern
else if(lowIdx > highIdx) {
double baseHigh = swingHighs[highIdx];
double dropLow = swingLows[lowIdx];
double dropSize = baseHigh - dropLow;
if(dropSize > minSwing) {
double currentClose = iClose(_Symbol, _Period, bar);
if(currentClose < dropLow - minDistance) {
double entry = NormalizeDouble(dropLow - minDistance, _Digits);
double sl = NormalizeDouble(baseHigh, _Digits);
double tp = NormalizeDouble(entry - (dropSize * RewardRatio), _Digits);
// Calculate stop level in price terms
double stopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL)
* SymbolInfoDouble(_Symbol, SYMBOL_POINT);
if(sl - entry > stopLevel) {
OpenTrade(ORDER_TYPE_SELL, entry, sl, tp);
}
}
}
}
}
//+------------------------------------------------------------------+
//| Execute trade with risk management |
//+------------------------------------------------------------------+
void OpenTrade(ENUM_ORDER_TYPE type, double entry, double sl, double tp)
{
if(iTime(_Symbol, _Period, 0) == lastTradeTime) return;
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskAmount = balance * RiskPercent / 100;
double pointValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE) /
SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double riskPoints = MathAbs(entry - sl) / SymbolInfoDouble(_Symbol,
SYMBOL_POINT);
double lotSize = NormalizeDouble(riskAmount / (riskPoints * pointValue), 2);
if(lotSize < SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN))
lotSize = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
if(lotSize > SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX))
lotSize = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
MqlTradeRequest request;
ZeroMemory(request);
MqlTradeResult result;
ZeroMemory(result);
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = lotSize;
request.type = type;
request.price = entry;
request.sl = sl;
request.tp = tp;
request.deviation = 10;
request.type_filling = ORDER_FILLING_FOK;
OrderSend(request, result);
if(result.retcode == TRADE_RETCODE_DONE) {
lastTradeTime = iTime(_Symbol, _Period, 0);
}
else {
Print("Trade error: ", GetRetcodeID(result.retcode));
}
}
//+------------------------------------------------------------------+
//| Get error message from return code |
//+------------------------------------------------------------------+
string GetRetcodeID(int retCode)
{
switch(retCode)
{
case 10004: return "TRADE_RETCODE_REQUOTE";
case 10006: return "TRADE_RETCODE_REJECT";
case 10007: return "TRADE_RETCODE_CANCEL";
case 10008: return "TRADE_RETCODE_PLACED";
case 10009: return "TRADE_RETCODE_DONE";
case 10010: return "TRADE_RETCODE_DONE_PARTIAL";
case 10011: return "TRADE_RETCODE_ERROR";
case 10012: return "TRADE_RETCODE_TIMEOUT";
case 10013: return "TRADE_RETCODE_INVALID";
case 10014: return "TRADE_RETCODE_INVALID_VOLUME";
case 10015: return "TRADE_RETCODE_INVALID_PRICE";
case 10016: return "TRADE_RETCODE_INVALID_STOPS";
case 10017: return "TRADE_RETCODE_TRADE_DISABLED";
case 10018: return "TRADE_RETCODE_MARKET_CLOSED";
case 10019: return "TRADE_RETCODE_NO_MONEY";
case 10020: return "TRADE_RETCODE_PRICE_CHANGED";
case 10021: return "TRADE_RETCODE_PRICE_OFF";
case 10022: return "TRADE_RETCODE_INVALID_EXPIRATION";
case 10023: return "TRADE_RETCODE_ORDER_CHANGED";
case 10024: return "TRADE_RETCODE_TOO_MANY_REQUESTS";
case 10025: return "TRADE_RETCODE_NO_CHANGES";
case 10026: return "TRADE_RETCODE_SERVER_DISABLES_AT";
case 10027: return "TRADE_RETCODE_CLIENT_DISABLES_AT";
case 10028: return "TRADE_RETCODE_LOCKED";
case 10029: return "TRADE_RETCODE_FROZEN";
case 10030: return "TRADE_RETCODE_INVALID_FILL";
case 10031: return "TRADE_RETCODE_CONNECTION";
case 10032: return "TRADE_RETCODE_ONLY_REAL";
case 10033: return "TRADE_RETCODE_LIMIT_ORDERS";
case 10034: return "TRADE_RETCODE_LIMIT_VOLUME";
default: return "UNKNOWN_ERROR";
}
}
//+------------------------------------------------------------------+