Code: Select all
//+------------------------------------------------------------------+
//| ProRiskDesk_MT4.mq4 |
//+------------------------------------------------------------------+
#property copyright "Risk Enforcer Pro"
#property strict
// --- Sunday Prep Inputs ---
input double MaxDailyLoss = 500.0; // Daily Loss Limit ($)
input double R_Value = 100.0; // Value of 1R ($)
input int MaxTrades = 10; // Max Trades Per Day
input int MaxStreak = 3; // Max Consecutive Losses
input bool FlashChart = true; // Override Chart Background on Halt
// --- State Variables ---
double startOfDayEquity = 0;
double peakDailyEquity = 0;
bool isHalted = false;
bool alertFired = false;
string haltReason = "";
color originalBgColor;
//+------------------------------------------------------------------+
//| Initialization |
//+------------------------------------------------------------------+
int OnInit() {
originalBgColor = (color)ChartGetInteger(0, CHART_COLOR_BACKGROUND);
startOfDayEquity = AccountInfoDouble(ACCOUNT_EQUITY);
peakDailyEquity = startOfDayEquity;
EventSetTimer(1);
DrawUI();
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason) {
EventKillTimer();
ObjectsDeleteAll(0, "RD_");
ChartSetInteger(0, CHART_COLOR_BACKGROUND, originalBgColor);
}
void OnTimer() { CheckRiskLimits(); }
void OnTick() { CheckRiskLimits(); }
//+------------------------------------------------------------------+
//| Core Logic |
//+------------------------------------------------------------------+
void CheckRiskLimits() {
datetime startOfDay = iTime(Symbol(), PERIOD_D1, 0);
// Reset state on new day
static datetime currentDay = 0;
if (startOfDay != currentDay) {
startOfDayEquity = AccountInfoDouble(ACCOUNT_EQUITY);
peakDailyEquity = startOfDayEquity;
isHalted = false;
alertFired = false;
haltReason = "";
currentDay = startOfDay;
ChartSetInteger(0, CHART_COLOR_BACKGROUND, originalBgColor);
}
// Update Peak Equity
double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
if (currentEquity > peakDailyEquity) peakDailyEquity = currentEquity;
double peakDrawdown = peakDailyEquity - currentEquity;
// Calculate Stats
int tradesToday = 0;
int consecLosses = 0;
double realizedPnL = 0;
// Track History
for(int i = 0; i < OrdersHistoryTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) {
if(OrderCloseTime() >= startOfDay && OrderType() <= OP_SELL) {
tradesToday++;
realizedPnL += OrderProfit() + OrderCommission() + OrderSwap();
}
}
}
// Track Consecutive Losses (Reverse loop)
for(int i = OrdersHistoryTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) {
if(OrderCloseTime() < startOfDay) break;
if(OrderType() <= OP_SELL) {
double prf = OrderProfit() + OrderCommission() + OrderSwap();
if(prf < 0) consecLosses++;
else break; // Streak broken
}
}
}
// Calculate Floating PnL
double floatingPnL = 0;
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
floatingPnL += OrderProfit() + OrderCommission() + OrderSwap();
}
}
double totalPnL = realizedPnL + floatingPnL;
double safeR = R_Value > 0 ? R_Value : 1;
double pnlInR = totalPnL / safeR;
// Check Halt Conditions
bool limitHit = (totalPnL <= -MaxDailyLoss);
bool tiltHit = (consecLosses >= MaxStreak);
bool overtradeHit = (tradesToday >= MaxTrades);
if (limitHit || tiltHit || overtradeHit) {
isHalted = true;
if(limitHit) haltReason = "HARD LIMIT HIT";
else if(tiltHit) haltReason = "TILT: MAX LOSS STREAK";
else haltReason = "OVERTRADING CAP HIT";
FlattenAccount();
if (!alertFired) {
Alert("TRADING HALTED: ", haltReason);
alertFired = true;
if (FlashChart) {
color bg = limitHit ? clrMaroon : clrPurple;
ChartSetInteger(0, CHART_COLOR_BACKGROUND, bg);
}
}
}
UpdateUI(totalPnL, pnlInR, peakDrawdown, tradesToday, consecLosses);
}
//+------------------------------------------------------------------+
//| Execution |
//+------------------------------------------------------------------+
void FlattenAccount() {
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderType() == OP_BUY) {
OrderClose(OrderTicket(), OrderLots(), MarketInfo(OrderSymbol(), MODE_BID), 3);
} else if(OrderType() == OP_SELL) {
OrderClose(OrderTicket(), OrderLots(), MarketInfo(OrderSymbol(), MODE_ASK), 3);
} else {
OrderDelete(OrderTicket());
}
}
}
}
//+------------------------------------------------------------------+
//| UI Engine |
//+------------------------------------------------------------------+
void DrawUI() {
CreateRect("RD_Bg", 20, 20, 220, 140, clrBlack);
CreateText("RD_Title", 130, 125, "RISK DESK: ACTIVE", clrWhite, 10, true);
CreateText("RD_L_PnL", 220, 100, "Daily PnL:", clrSilver, 9);
CreateText("RD_V_PnL", 40, 100, "$0.00", clrLime, 9);
CreateText("RD_L_R", 220, 80, "Target (R):", clrSilver, 9);
CreateText("RD_V_R", 40, 80, "0.00 R", clrLime, 9);
CreateText("RD_L_DD", 220, 60, "Drawdown:", clrSilver, 9);
CreateText("RD_V_DD", 40, 60, "$0.00", clrGray, 9);
CreateText("RD_L_Trd", 220, 40, "Trades:", clrSilver, 9);
CreateText("RD_V_Trd", 40, 40, "0 / 0", clrWhite, 9);
CreateText("RD_L_Str", 220, 20, "Loss Streak:", clrSilver, 9);
CreateText("RD_V_Str", 40, 20, "0 / 0", clrWhite, 9);
}
void CreateRect(string name, int x, int y, int w, int h, color bg) {
ObjectCreate(0, name, OBJ_RECTANGLE_LABEL, 0, 0, 0);
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
ObjectSetInteger(0, name, OBJPROP_XSIZE, w);
ObjectSetInteger(0, name, OBJPROP_YSIZE, h);
ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bg);
ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, clrDimGray);
}
void CreateText(string name, int x, int y, string text, color clr, int size, bool bold=false) {
ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
ObjectSetString(0, name, OBJPROP_FONT, bold ? "Courier New Bold" : "Courier New");
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, size);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetString(0, name, OBJPROP_TEXT, text);
}
void UpdateUI(double pnl, double r, double dd, int trd, int str) {
ObjectSetString(0, "RD_Title", OBJPROP_TEXT, isHalted ? haltReason : "RISK DESK: ACTIVE");
ObjectSetInteger(0, "RD_Title", OBJPROP_COLOR, isHalted ? clrWhite : clrAqua);
ObjectSetInteger(0, "RD_Bg", OBJPROP_BGCOLOR, isHalted ? clrMaroon : clrBlack);
ObjectSetString(0, "RD_V_PnL", OBJPROP_TEXT, (pnl >= 0 ? "+$" : "-$") + DoubleToStr(MathAbs(pnl), 2));
ObjectSetInteger(0, "RD_V_PnL", OBJPROP_COLOR, pnl >= 0 ? clrLime : clrRed);
ObjectSetString(0, "RD_V_R", OBJPROP_TEXT, (r >= 0 ? "+" : "") + DoubleToStr(r, 2) + " R");
ObjectSetInteger(0, "RD_V_R", OBJPROP_COLOR, r >= 0 ? clrLime : clrRed);
ObjectSetString(0, "RD_V_DD", OBJPROP_TEXT, "$" + DoubleToStr(dd, 2));
ObjectSetString(0, "RD_V_Trd", OBJPROP_TEXT, IntegerToString(trd) + " / " + IntegerToString(MaxTrades));
ObjectSetInteger(0, "RD_V_Trd", OBJPROP_COLOR, trd >= MaxTrades ? clrRed : clrWhite);
ObjectSetString(0, "RD_V_Str", OBJPROP_TEXT, IntegerToString(str) + " / " + IntegerToString(MaxStreak));
ObjectSetInteger(0, "RD_V_Str", OBJPROP_COLOR, str >= MaxStreak ? clrRed : clrWhite);
ChartRedraw();
}