MQL5 allows for cleaner input groupings. This version utilizes input group for a sleek properties menu and fetches ticks robustly using SymbolInfoDouble().
Code: Select all
//+------------------------------------------------------------------+
//| Pro_NetExpectancyFriction.mq5 |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_plots 0
// =========================================================================
// INPUTS
// =========================================================================
input group "--- Strategy Metrics ---";
input double InpWinRate = 55.0; // Historical Win Rate (%)
input double InpMeanGrossWin = 4.0; // Mean Gross Win (Pips)
input double InpMeanGrossLoss = 3.0; // Mean Gross Loss (Pips)
input group "--- Execution Friction ---";
input double InpCommPerLot = 7.0; // Round Turn Comm ($/Lot)
input double InpPipValue = 10.0; // Pip Value ($/Lot)
input double InpEstSlippage = 0.2; // Mean Slippage (Pips)
// Globals
double pipSize;
string prefix = "ProExpDash_";
//+------------------------------------------------------------------+
//| Initialization |
//+------------------------------------------------------------------+
int OnInit() {
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
pipSize = point;
if (digits == 3 || digits == 5) pipSize *= 10.0;
if (pipSize == 0.0) {
Print("Initialization Error: Division by zero risk (pipSize = 0).");
return(INIT_FAILED);
}
DrawBackground();
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Deinitialization |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
ObjectsDeleteAll(0, prefix);
ChartRedraw();
}
//+------------------------------------------------------------------+
//| Main Calculation Iteration |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
// Fetch live tick data safely
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
// Execution Math
double liveSpreadPips = (ask - bid) / pipSize;
double wr = InpWinRate / 100.0;
double commInPips = InpCommPerLot / InpPipValue;
double totalFriction = commInPips + liveSpreadPips + InpEstSlippage;
// Expectancy Math
double netWinMean = InpMeanGrossWin - totalFriction;
double netLossMean = InpMeanGrossLoss + totalFriction;
double grossExp = (InpMeanGrossWin * wr) - (InpMeanGrossLoss * (1.0 - wr));
double netExp = (netWinMean * wr) - (netLossMean * (1.0 - wr));
// UI Rendering
color netColor = (netExp > 0) ? clrLime : clrRed;
DrawRow(1, "Live Spread", DoubleToString(liveSpreadPips, 2), clrAqua);
DrawRow(2, "Execution Drag", DoubleToString(totalFriction, 2), clrRed);
DrawRow(3, "Gross Alpha", DoubleToString(grossExp, 2), clrSilver);
DrawRow(4, "Net Yield (1x)", DoubleToString(netExp, 2), netColor);
DrawRow(5, "Net Yield (100x)", DoubleToString(netExp * 100.0, 2), netColor);
return(rates_total);
}
//+------------------------------------------------------------------+
//| GUI: Draw Background Panel |
//+------------------------------------------------------------------+
void DrawBackground() {
string bgName = prefix + "BG";
if(ObjectFind(0, bgName) < 0) {
ObjectCreate(0, bgName, OBJ_RECTANGLE_LABEL, 0, 0, 0);
ObjectSetInteger(0, bgName, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
ObjectSetInteger(0, bgName, OBJPROP_XDISTANCE, 10);
ObjectSetInteger(0, bgName, OBJPROP_YDISTANCE, 20);
ObjectSetInteger(0, bgName, OBJPROP_XSIZE, 240);
ObjectSetInteger(0, bgName, OBJPROP_YSIZE, 135);
ObjectSetInteger(0, bgName, OBJPROP_BGCOLOR, clrBlack);
ObjectSetInteger(0, bgName, OBJPROP_BORDER_TYPE, BORDER_FLAT);
ObjectSetInteger(0, bgName, OBJPROP_COLOR, clrDimGray);
ObjectSetInteger(0, bgName, OBJPROP_BACK, true);
ObjectSetInteger(0, bgName, OBJPROP_SELECTABLE, false);
}
}
//+------------------------------------------------------------------+
//| GUI: Draw Data Row |
//+------------------------------------------------------------------+
void DrawRow(int row, string label, string val, color valColor) {
int yOffset = (row * 20) + 15;
string lblName = prefix + "lbl_" + IntegerToString(row);
string valName = prefix + "val_" + IntegerToString(row);
// Label (Left Aligned)
if(ObjectFind(0, lblName) < 0) {
ObjectCreate(0, lblName, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, lblName, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
ObjectSetInteger(0, lblName, OBJPROP_ANCHOR, ANCHOR_RIGHT_LOWER);
ObjectSetInteger(0, lblName, OBJPROP_XDISTANCE, 230);
ObjectSetString(0, lblName, OBJPROP_FONT, "Arial");
ObjectSetInteger(0, lblName, OBJPROP_FONTSIZE, 9);
ObjectSetInteger(0, lblName, OBJPROP_COLOR, clrLightGray);
ObjectSetInteger(0, lblName, OBJPROP_SELECTABLE, false);
}
ObjectSetInteger(0, lblName, OBJPROP_YDISTANCE, yOffset);
ObjectSetString(0, lblName, OBJPROP_TEXT, label + ":");
// Value (Right Aligned, Monospaced)
if(ObjectFind(0, valName) < 0) {
ObjectCreate(0, valName, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, valName, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
ObjectSetInteger(0, valName, OBJPROP_ANCHOR, ANCHOR_RIGHT_LOWER);
ObjectSetInteger(0, valName, OBJPROP_XDISTANCE, 20);
ObjectSetString(0, valName, OBJPROP_FONT, "Consolas");
ObjectSetInteger(0, valName, OBJPROP_FONTSIZE, 9);
ObjectSetInteger(0, valName, OBJPROP_SELECTABLE, false);
}
ObjectSetInteger(0, valName, OBJPROP_YDISTANCE, yOffset);
ObjectSetInteger(0, valName, OBJPROP_COLOR, valColor);
ObjectSetString(0, valName, OBJPROP_TEXT, val);
}