Since TradingView's interactive input.price() doesn't translate 1:1 to MetaTrader, this MQL5 utility runs as a custom Indicator. Upon initialization, it generates three interactive horizontal lines (Requested Entry, SL, TP) on your chart. As you drag them to map your structure, the OnChartEvent handler instantly calculates the execution drag, plots the broker's realized fill lines (as read-only dashed lines), and updates a live telemetry HUD via Comment().
It natively auto-detects direction (based on TP placement relative to Entry) and sizes the position using SYMBOL_TRADE_TICK_VALUE to ensure perfect lot sizing across spot forex, XAUUSD, and equities.
Code: Select all
//+------------------------------------------------------------------+
//| Execution_Drag_Modeler.mq5 |
//| Cost-Adjusted Expectancy |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_plots 0
//--- Inputs
input string Grp1 = "--- Risk Parameters ---";
input double InpRiskPercent = 1.0; // Risk Per Trade (%)
input string Grp2 = "--- Execution Costs (Points) ---";
input int InpSpreadPoints = 15; // Average Spread at Click (Points)
input int InpSlippageInPoints = 5; // Negative Slippage Entry (Points)
input int InpSlippageOutPoints = 10; // Negative Slippage Exit (SL/TP) (Points)
input string Grp3 = "--- Visuals ---";
input color ColorReqEntry = clrGray;
input color ColorReqSL = clrMaroon;
input color ColorReqTP = clrDarkGreen;
input color ColorRealEntry = clrDodgerBlue;
input color ColorRealSL = clrRed;
input color ColorRealTP = clrTeal;
//--- Object Names
string L_REQ_ENTRY = "Req_Entry_Line";
string L_REQ_SL = "Req_SL_Line";
string L_REQ_TP = "Req_TP_Line";
string L_REAL_ENTRY = "Real_Entry_Line";
string L_REAL_SL = "Real_SL_Line";
string L_REAL_TP = "Real_TP_Line";
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Initialize Requested Lines (Interactive)
double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double atr = iATR(_Symbol, PERIOD_CURRENT, 14);
// Create interactive lines if they don't exist
CreateDraggableLine(L_REQ_ENTRY, currentPrice, ColorReqEntry, "Requested Entry");
CreateDraggableLine(L_REQ_SL, currentPrice - (50 * _Point), ColorReqSL, "Requested SL");
CreateDraggableLine(L_REQ_TP, currentPrice + (100 * _Point), ColorReqTP, "Requested TP");
// Create Realized Lines (Read-Only)
CreateReadOnlyLine(L_REAL_ENTRY, ColorRealEntry, STYLE_DASH);
CreateReadOnlyLine(L_REAL_SL, ColorRealSL, STYLE_DASH);
CreateReadOnlyLine(L_REAL_TP, ColorRealTP, STYLE_DASH);
CalculateAndDisplay();
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ObjectsDeleteAll(0, L_REQ_ENTRY);
ObjectsDeleteAll(0, L_REQ_SL);
ObjectsDeleteAll(0, L_REQ_TP);
ObjectsDeleteAll(0, L_REAL_ENTRY);
ObjectsDeleteAll(0, L_REAL_SL);
ObjectsDeleteAll(0, L_REAL_TP);
Comment("");
}
//+------------------------------------------------------------------+
//| ChartEvent function for interactive dragging |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
if(id == CHARTEVENT_OBJECT_DRAG)
{
CalculateAndDisplay();
}
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
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[])
{
// Update live if needed, but ChartEvent handles interaction
return(rates_total);
}
//+------------------------------------------------------------------+
//| Core Math & Display Logic |
//+------------------------------------------------------------------+
void CalculateAndDisplay()
{
double req_entry = ObjectGetDouble(0, L_REQ_ENTRY, OBJPROP_PRICE);
double req_sl = ObjectGetDouble(0, L_REQ_SL, OBJPROP_PRICE);
double req_tp = ObjectGetDouble(0, L_REQ_TP, OBJPROP_PRICE);
// Auto-detect direction
bool is_long = (req_tp > req_entry);
// Convert costs to price values
double spread_cost = InpSpreadPoints * _Point;
double slip_in_cost = InpSlippageInPoints * _Point;
double slip_out_cost = InpSlippageOutPoints * _Point;
// Realized levels
double real_entry = is_long ? req_entry + spread_cost + slip_in_cost : req_entry - spread_cost - slip_in_cost;
double real_sl = is_long ? req_sl - slip_out_cost : req_sl + slip_out_cost;
double real_tp = is_long ? req_tp - slip_out_cost : req_tp + slip_out_cost;
// Update Realized Lines on chart
ObjectSetDouble(0, L_REAL_ENTRY, OBJPROP_PRICE, real_entry);
ObjectSetDouble(0, L_REAL_SL, OBJPROP_PRICE, real_sl);
ObjectSetDouble(0, L_REAL_TP, OBJPROP_PRICE, real_tp);
// Theoretical R
double theo_risk = MathAbs(req_entry - req_sl);
double theo_reward = MathAbs(req_tp - req_entry);
double theo_r = (theo_risk > 0) ? (theo_reward / theo_risk) : 0;
// Realized R
double real_risk = MathAbs(real_entry - real_sl);
double real_reward = MathAbs(real_tp - real_entry);
double real_r = (real_risk > 0) ? (real_reward / real_risk) : 0;
// Metrics
double r_bleed_pct = (theo_r > 0) ? ((theo_r - real_r) / theo_r) * 100.0 : 0.0;
double risk_inflation = (real_risk > 0 && theo_risk > 0) ? ((real_risk / theo_risk) - 1.0) * 100.0 : 0.0;
double mvwr = (real_r > 0) ? (1.0 / (1.0 + real_r)) * 100.0 : 100.0;
// Position Sizing (using true tick values)
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double risk_money = balance * (InpRiskPercent / 100.0);
double tick_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double min_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double max_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lot_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double risk_ticks = real_risk / tick_size;
double raw_lots = (risk_ticks > 0 && tick_value > 0) ? (risk_money / (risk_ticks * tick_value)) : 0;
// Normalize lot size
double final_lots = MathFloor(raw_lots / lot_step) * lot_step;
final_lots = MathMax(min_lot, MathMin(final_lots, max_lot));
// Format HUD
string direction = is_long ? "LONG" : "SHORT";
string hud = StringFormat(
"--- EXECUTION TELEMETRY (%s - %s) ---\n" +
"Capital at Risk: $%.2f (%.1f%%)\n\n" +
"Theoretical R: %.2fR\n" +
"Realized Net R: %.2fR\n" +
"Expectancy Bleed: -%.1f%%\n\n" +
"Risk Inflation Tax: +%.1f%%\n" +
"Min Viable Win Rate: %.1f%%\n\n" +
">>> ADJUSTED SIZE: %.2f Lots <<<",
_Symbol, direction, risk_money, InpRiskPercent,
theo_r, real_r, r_bleed_pct, risk_inflation, mvwr, final_lots
);
Comment(hud);
}
//+------------------------------------------------------------------+
//| Helpers |
//+------------------------------------------------------------------+
void CreateDraggableLine(string name, double price, color clr, string tooltip)
{
if(ObjectFind(0, name) < 0)
{
ObjectCreate(0, name, OBJ_HLINE, 0, 0, price);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(0, name, OBJPROP_WIDTH, 2);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, true);
ObjectSetInteger(0, name, OBJPROP_SELECTED, true); // Ready to drag
ObjectSetString(0, name, OBJPROP_TOOLTIP, tooltip);
}
}
void CreateReadOnlyLine(string name, color clr, ENUM_LINE_STYLE style)
{
if(ObjectFind(0, name) < 0)
{
ObjectCreate(0, name, OBJ_HLINE, 0, 0, 0);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_STYLE, style);
ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
ObjectSetString(0, name, OBJPROP_TOOLTIP, "\n");
}
}