Code: Select all
//+------------------------------------------------------------------+
//| RiskDesk_Cap_Pro.mq5 |
//| Institutional Risk Desk with Persistence |
//+------------------------------------------------------------------+
#property copyright "Risk Desk Professional"
#include <Trade\Trade.mqh>
CTrade trade;
// --- Risk Desk Inputs ---
input group "--- Risk Enforcement ---"
input int MaxIdeas = 3; // Daily Idea Cap
input int MaxClicks = 10; // Daily Execution Cap
input double DailyMaxLossUSD = 500.0; // Daily Drawdown Hard Stop ($)
input bool NewsOverride = false; // Pre-Planned News Exemption
input ulong MagicNumber = 77777;
input group "--- Strategy ---"
input double LotSize = 0.1;
input int RsiPeriod = 14;
// --- State Variables ---
color originalBG;
string gvIdeas, gvClicks, gvDay;
int dailyIdeas = 0;
int dailyClicks = 0;
double dailyPnL = 0;
int rsiHandle;
double rsiBuffer[];
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit() {
trade.SetExpertMagicNumber(MagicNumber);
originalBG = (color)ChartGetInteger(0, CHART_COLOR_BACKGROUND);
// Global Variable Names Setup
string prefix = "RiskDesk_" + _Symbol + "_" + IntegerToString(MagicNumber) + "_";
gvIdeas = prefix + "Ideas";
gvClicks = prefix + "Clicks";
gvDay = prefix + "Day";
MqlDateTime dt; TimeCurrent(dt);
// State Recovery
if(GlobalVariableCheck(gvDay) && GlobalVariableGet(gvDay) == dt.day) {
if(GlobalVariableCheck(gvIdeas)) dailyIdeas = (int)GlobalVariableGet(gvIdeas);
if(GlobalVariableCheck(gvClicks)) dailyClicks = (int)GlobalVariableGet(gvClicks);
} else {
ResetDailyState(dt.day);
}
rsiHandle = iRSI(_Symbol, _Period, RsiPeriod, PRICE_CLOSE);
ArraySetAsSeries(rsiBuffer, true);
CreateBlotter();
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason) {
ChartSetInteger(0, CHART_COLOR_BACKGROUND, originalBG);
ObjectsDeleteAll(0, "DeskBlotter_");
}
void ResetDailyState(int currentDay) {
dailyIdeas = 0; dailyClicks = 0;
GlobalVariableSet(gvDay, currentDay);
GlobalVariableSet(gvIdeas, 0);
GlobalVariableSet(gvClicks, 0);
}
//+------------------------------------------------------------------+
//| Daily PnL Calculation (MT5 Deal History) |
//+------------------------------------------------------------------+
void CalculateDailyPnL() {
dailyPnL = 0;
// 1. Fetch Closed Deals for Today
datetime startOfDay = iTime(_Symbol, PERIOD_D1, 0);
datetime endOfDay = TimeCurrent() + 86400;
if(HistorySelect(startOfDay, endOfDay)) {
for(int i=0; i<HistoryDealsTotal(); i++) {
ulong dealTicket = HistoryDealGetTicket(i);
if(HistoryDealGetInteger(dealTicket, DEAL_MAGIC) == MagicNumber && HistoryDealGetString(dealTicket, DEAL_SYMBOL) == _Symbol) {
dailyPnL += HistoryDealGetDouble(dealTicket, DEAL_PROFIT) + HistoryDealGetDouble(dealTicket, DEAL_COMMISSION) + HistoryDealGetDouble(dealTicket, DEAL_SWAP);
}
}
}
// 2. Add Floating PnL
for(int i = PositionsTotal()-1; i >= 0; i--) {
ulong posTicket = PositionGetTicket(i);
if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber) {
dailyPnL += PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP);
}
}
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick() {
MqlDateTime dt; TimeCurrent(dt);
if(GlobalVariableGet(gvDay) != dt.day) ResetDailyState(dt.day);
CalculateDailyPnL();
int openBuys = 0, openSells = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber) {
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) openBuys++;
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) openSells++;
}
}
bool isFlat = (openBuys == 0 && openSells == 0);
bool lossLimitHit = (dailyPnL <= -MathAbs(DailyMaxLossUSD));
bool capReached = (dailyIdeas >= MaxIdeas) || (dailyClicks >= MaxClicks) || lossLimitHit;
bool warningLevel = (dailyIdeas == MaxIdeas - 1) || (dailyPnL <= -(MathAbs(DailyMaxLossUSD) * 0.8));
bool isHardStop = capReached && !NewsOverride;
bool isSoftWarning = warningLevel && !capReached;
bool canTakeNewIdea = !isHardStop;
bool canScaleIn = (dailyClicks < MaxClicks && !lossLimitHit) || NewsOverride;
UpdateBlotter(isHardStop, isSoftWarning, lossLimitHit);
if(CopyBuffer(rsiHandle, 0, 0, 2, rsiBuffer) <= 0) return;
bool longSignal = (rsiBuffer[1] < 30 && rsiBuffer[0] >= 30);
bool shortSignal = (rsiBuffer[1] > 70 && rsiBuffer[0] <= 70);
bool flatSignal = ( (openBuys > 0 && rsiBuffer[0] >= 50) || (openSells > 0 && rsiBuffer[0] <= 50) );
// FORCE CLOSE IF LIMIT HIT OR EXIT SIGNAL
if(flatSignal || (lossLimitHit && !isFlat)) {
for(int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber) {
trade.PositionClose(ticket);
}
}
}
if(isFlat && canTakeNewIdea) {
if(longSignal && trade.Buy(LotSize, _Symbol, 0, 0, 0, "New Idea")) {
dailyIdeas++; dailyClicks++;
GlobalVariableSet(gvIdeas, dailyIdeas); GlobalVariableSet(gvClicks, dailyClicks);
}
else if(shortSignal && trade.Sell(LotSize, _Symbol, 0, 0, 0, "New Idea")) {
dailyIdeas++; dailyClicks++;
GlobalVariableSet(gvIdeas, dailyIdeas); GlobalVariableSet(gvClicks, dailyClicks);
}
}
else if(!isFlat && canScaleIn) {
if(longSignal && openBuys > 0 && trade.Buy(LotSize, _Symbol, 0, 0, 0, "Scale")) {
dailyClicks++; GlobalVariableSet(gvClicks, dailyClicks);
}
else if(shortSignal && openSells > 0 && trade.Sell(LotSize, _Symbol, 0, 0, 0, "Scale")) {
dailyClicks++; GlobalVariableSet(gvClicks, dailyClicks);
}
}
}
//+------------------------------------------------------------------+
//| UI GUI Panel Functions |
//+------------------------------------------------------------------+
void CreateBlotter() {
ObjectCreate(0, "DeskBlotter_BG", OBJ_RECTANGLE_LABEL, 0, 0, 0);
ObjectSetInteger(0, "DeskBlotter_BG", OBJPROP_CORNER, CORNER_RIGHT_LOWER);
ObjectSetInteger(0, "DeskBlotter_BG", OBJPROP_XDISTANCE, 10);
ObjectSetInteger(0, "DeskBlotter_BG", OBJPROP_YDISTANCE, 10);
ObjectSetInteger(0, "DeskBlotter_BG", OBJPROP_XSIZE, 180);
ObjectSetInteger(0, "DeskBlotter_BG", OBJPROP_YSIZE, 110);
ObjectSetInteger(0, "DeskBlotter_BG", OBJPROP_BGCOLOR, clrBlack);
ObjectSetInteger(0, "DeskBlotter_BG", OBJPROP_BORDER_TYPE, BORDER_FLAT);
ObjectSetInteger(0, "DeskBlotter_BG", OBJPROP_COLOR, clrDimGray);
ObjectSetInteger(0, "DeskBlotter_BG", OBJPROP_BACK, true);
string labels[5] = {"Header", "Ideas", "Clicks", "PnL", "Status"};
for(int i=0; i<5; i++) {
string name = "DeskBlotter_" + labels[i];
ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, 20);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, 90 - (i * 20));
ObjectSetString(0, name, OBJPROP_FONT, "Trebuchet MS");
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 9);
}
}
void UpdateBlotter(bool isHardStop, bool isSoftWarning, bool lossLimitHit) {
color bg = NewsOverride ? clrMidnightBlue : (isHardStop ? clrMaroon : (isSoftWarning ? clrSaddleBrown : originalBG));
ChartSetInteger(0, CHART_COLOR_BACKGROUND, bg);
string status = NewsOverride ? "NEWS EXEMPTION" : (lossLimitHit ? "MAX LOSS HIT" : (isHardStop ? "LOCKED (Walk Away)" : (isSoftWarning ? "A+ SETUPS ONLY" : "ACTIVE")));
color statClr = NewsOverride ? clrDeepSkyBlue : (isHardStop ? clrRed : (isSoftWarning ? clrOrange : clrLimeGreen));
ObjectSetString(0, "DeskBlotter_Header", OBJPROP_TEXT, "--- RISK DESK ---");
ObjectSetInteger(0, "DeskBlotter_Header", OBJPROP_COLOR, clrSilver);
ObjectSetString(0, "DeskBlotter_Ideas", OBJ, PROP_TEXT, "Ideas: " + IntegerToString(dailyIdeas) + " / " + IntegerToString(MaxIdeas));
ObjectSetInteger(0, "DeskBlotter_Ideas", OBJPROP_COLOR, (dailyIdeas >= MaxIdeas) ? clrRed : clrWhite);
ObjectSetString(0, "DeskBlotter_Clicks", OBJPROP_TEXT, "Clicks: " + IntegerToString(dailyClicks) + " / " + IntegerToString(MaxClicks));
ObjectSetInteger(0, "DeskBlotter_Clicks", OBJPROP_COLOR, (dailyClicks >= MaxClicks) ? clrRed : clrWhite);
ObjectSetString(0, "DeskBlotter_PnL", OBJPROP_TEXT, "Daily PnL: $" + DoubleToString(dailyPnL, 2));
ObjectSetInteger(0, "DeskBlotter_PnL", OBJPROP_COLOR, (dailyPnL < 0) ? clrRed : clrLimeGreen);
ObjectSetString(0, "DeskBlotter_Status", OBJPROP_TEXT, status);
ObjectSetInteger(0, "DeskBlotter_Status", OBJPROP_COLOR, statClr);
}