Code: Select all
//+------------------------------------------------------------------+
//| RiskDesk_Cap_Pro.mq4 |
//| Institutional Risk Desk with Persistence |
//+------------------------------------------------------------------+
#property copyright "Risk Desk Professional"
#property strict
// --- Risk Desk Inputs ---
input string Grp1 = "--- 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 int MagicNumber = 77777;
// --- Strategy Inputs ---
input string Grp2 = "--- Strategy ---";
input double LotSize = 0.1;
input int RsiPeriod = 14;
// --- State Management ---
color originalBG;
string gvIdeas, gvClicks, gvDay;
int dailyIdeas = 0;
int dailyClicks = 0;
double dailyPnL = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit() {
originalBG = (color)ChartGetInteger(0, CHART_COLOR_BACKGROUND);
// Setup Global Variable Names (Unique to Symbol & Magic Number)
string prefix = "RiskDesk_" + Symbol() + "_" + IntegerToString(MagicNumber) + "_";
gvIdeas = prefix + "Ideas";
gvClicks = prefix + "Clicks";
gvDay = prefix + "Day";
// Load State from Hard Drive
if(GlobalVariableCheck(gvDay) && GlobalVariableGet(gvDay) == Day()) {
if(GlobalVariableCheck(gvIdeas)) dailyIdeas = (int)GlobalVariableGet(gvIdeas);
if(GlobalVariableCheck(gvClicks)) dailyClicks = (int)GlobalVariableGet(gvClicks);
} else {
ResetDailyState();
}
CreateBlotter();
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason) {
ChartSetInteger(0, CHART_COLOR_BACKGROUND, originalBG);
ObjectsDeleteAll(0, "DeskBlotter_");
}
//+------------------------------------------------------------------+
//| Helper: Reset Daily State & Save |
//+------------------------------------------------------------------+
void ResetDailyState() {
dailyIdeas = 0;
dailyClicks = 0;
GlobalVariableSet(gvDay, Day());
GlobalVariableSet(gvIdeas, 0);
GlobalVariableSet(gvClicks, 0);
}
//+------------------------------------------------------------------+
//| Helper: Calculate Daily PnL |
//+------------------------------------------------------------------+
void CalculateDailyPnL() {
dailyPnL = 0;
// 1. Closed PnL for today
for(int i = OrdersHistoryTotal()-1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber) {
if(TimeDay(OrderCloseTime()) == Day() && TimeMonth(OrderCloseTime()) == Month()) {
dailyPnL += OrderProfit() + OrderCommission() + OrderSwap();
}
}
}
}
// 2. Floating PnL
for(int i = OrdersTotal()-1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber) {
dailyPnL += OrderProfit() + OrderCommission() + OrderSwap();
}
}
}
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick() {
if(GlobalVariableGet(gvDay) != Day()) ResetDailyState();
CalculateDailyPnL();
// Position Assessment
int openBuys = 0, openSells = 0;
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber) {
if(OrderType() == OP_BUY) openBuys++;
if(OrderType() == OP_SELL) openSells++;
}
}
bool isFlat = (openBuys == 0 && openSells == 0);
// Enforcement Logic
bool lossLimitHit = (dailyPnL <= -MathAbs(DailyMaxLossUSD));
bool capReached = (dailyIdeas >= MaxIdeas) || (dailyClicks >= MaxClicks) || lossLimitHit;
bool warningLevel = (dailyIdeas == MaxIdeas - 1) || (dailyPnL <= -(MathAbs(DailyMaxLossUSD) * 0.8)); // 80% loss warning
bool isHardStop = capReached && !NewsOverride;
bool isSoftWarning = warningLevel && !capReached;
bool canTakeNewIdea = !isHardStop;
bool canScaleIn = (dailyClicks < MaxClicks && !lossLimitHit) || NewsOverride;
UpdateBlotter(isHardStop, isSoftWarning, lossLimitHit);
// Strategy Execution (RSI Placeholder)
double rsi0 = iRSI(NULL, 0, RsiPeriod, PRICE_CLOSE, 0);
double rsi1 = iRSI(NULL, 0, RsiPeriod, PRICE_CLOSE, 1);
bool longSignal = (rsi1 < 30 && rsi0 >= 30);
bool shortSignal = (rsi1 > 70 && rsi0 <= 70);
bool flatSignal = ( (openBuys > 0 && rsi0 >= 50) || (openSells > 0 && rsi0 <= 50) );
if(flatSignal || (lossLimitHit && !isFlat)) { // Force close if max loss hit
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber) {
if(OrderType() == OP_BUY) OrderClose(OrderTicket(), OrderLots(), Bid, 3, clrWhite);
if(OrderType() == OP_SELL) OrderClose(OrderTicket(), OrderLots(), Ask, 3, clrWhite);
}
}
}
if(isFlat && canTakeNewIdea) {
if(longSignal && OrderSend(Symbol(), OP_BUY, LotSize, Ask, 3, 0, 0, "New Idea", MagicNumber, 0, clrGreen) > 0) {
dailyIdeas++; dailyClicks++;
GlobalVariableSet(gvIdeas, dailyIdeas); GlobalVariableSet(gvClicks, dailyClicks);
}
else if(shortSignal && OrderSend(Symbol(), OP_SELL, LotSize, Bid, 3, 0, 0, "New Idea", MagicNumber, 0, clrRed) > 0) {
dailyIdeas++; dailyClicks++;
GlobalVariableSet(gvIdeas, dailyIdeas); GlobalVariableSet(gvClicks, dailyClicks);
}
}
else if(!isFlat && canScaleIn) {
if(longSignal && openBuys > 0 && OrderSend(Symbol(), OP_BUY, LotSize, Ask, 3, 0, 0, "Scale", MagicNumber, 0, clrGreen) > 0) {
dailyClicks++; GlobalVariableSet(gvClicks, dailyClicks);
}
else if(shortSignal && openSells > 0 && OrderSend(Symbol(), OP_SELL, LotSize, Bid, 3, 0, 0, "Scale", MagicNumber, 0, clrRed) > 0) {
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", OBJPROP_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);
}