Code: Select all
//+------------------------------------------------------------------+
//| ProExecutionModel.mq5 |
//| Institutional Cost Model w/ GUI |
//+------------------------------------------------------------------+
#property copyright "Pro Execution Model"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots 2
// Plot Long Breakeven
#property indicator_label1 "Long Breakeven"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrTeal
#property indicator_style1 STYLE_DASH
#property indicator_width1 1
// Plot Short Breakeven
#property indicator_label2 "Short Breakeven"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrMaroon
#property indicator_style2 STYLE_DASH
#property indicator_width2 1
// --- Inputs ---
input string grp1 = "--- Position & Fees ---";
input double InpLots = 1.0; // Position Size (Lots)
input int InpFeeType = 0; // Fee Type: 0=Per Lot, 1=Percentage
input double InpFeeRate = 3.5; // Fee Rate (Round Trip per Lot or %)
input double InpFixedFee = 0.0; // Fixed Ticket Fee ($)
input string grp2 = "--- Market Impact ---";
input bool InpUseLiveSpread = true; // Use Live Broker Spread? (Overrides Base)
input int InpBaseSpread = 10; // Base Spread (if Live is false)
input int InpBaseSlippage = 10; // Base Slippage Per Side (in Points)
input bool InpUseDynSlip = true; // Use Volatility-Adjusted Slippage (ATR)
input double InpSlipFactor = 0.05; // ATR Slippage Factor
// --- Buffers ---
double LongBEBuffer[];
double ShortBEBuffer[];
// --- Global Variables ---
int atr_handle;
string prefix = "PRO_EDM_";
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, LongBEBuffer, INDICATOR_DATA);
SetIndexBuffer(1, ShortBEBuffer, INDICATOR_DATA);
atr_handle = iATR(_Symbol, PERIOD_CURRENT, 14);
if(atr_handle == INVALID_HANDLE) return(INIT_FAILED);
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason) { ObjectsDeleteAll(0, prefix); IndicatorRelease(atr_handle); }
//+------------------------------------------------------------------+
//| 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[])
{
if(rates_total < 14) return(0);
double atr_array[];
if(CopyBuffer(atr_handle, 0, 0, rates_total, atr_array) <= 0) return(0);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
double tick_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double tick_val = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double point_val = tick_val * (point / tick_size);
if(point_val == 0) point_val = 1;
double contract_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE);
int start = prev_calculated == 0 ? 0 : prev_calculated - 1;
for(int i = start; i < rates_total; i++)
{
double current_close = close[i];
double notional = InpLots * contract_size * current_close;
// Dynamic ATR Slippage
double dyn_slip = InpBaseSlippage;
if(InpUseDynSlip) dyn_slip += ((atr_array[i] / point) * InpSlipFactor);
// Live vs Manual Spread
int current_spread = InpBaseSpread;
if(InpUseLiveSpread) current_spread = (int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
// Core Costs
double commCost = (InpFeeType == 0) ? (InpFeeRate * InpLots) + InpFixedFee : (notional * (InpFeeRate / 100)) + InpFixedFee;
double spreadCost = current_spread * point_val * InpLots;
double slipCost = (dyn_slip * 2) * point_val * InpLots;
double totalCost = commCost + spreadCost + slipCost;
// Breakeven Math
double pointsToBE = totalCost / (point_val * InpLots);
LongBEBuffer[i] = current_close + (pointsToBE * point);
ShortBEBuffer[i] = current_close - (pointsToBE * point);
// UI Update on live tick
if(i == rates_total - 1)
{
double acct_bal = AccountInfoDouble(ACCOUNT_BALANCE);
double acct_drag = (acct_bal > 0) ? (totalCost / acct_bal) * 100 : 0;
DrawPanel(notional, commCost, spreadCost, slipCost, totalCost, acct_drag, pointsToBE, InpUseLiveSpread);
}
}
return(rates_total);
}
//+------------------------------------------------------------------+
//| Graphical UI Panel Construction |
//+------------------------------------------------------------------+
void DrawPanel(double notional, double comm, double spread, double slip, double total, double drag, double be_points, bool liveSpread)
{
int baseX = 20, baseY = 20, w = 310, h = 210, pad = 15, rowH = 22;
string ccy = AccountInfoString(ACCOUNT_CURRENCY);
// Background Panel
if(ObjectFind(0, prefix+"_BG") < 0) ObjectCreate(0, prefix+"_BG", OBJ_RECTANGLE_LABEL, 0, 0, 0);
ObjectSetInteger(0, prefix+"_BG", OBJPROP_XDISTANCE, baseX);
ObjectSetInteger(0, prefix+"_BG", OBJPROP_YDISTANCE, baseY);
ObjectSetInteger(0, prefix+"_BG", OBJPROP_XSIZE, w);
ObjectSetInteger(0, prefix+"_BG", OBJPROP_YSIZE, h);
ObjectSetInteger(0, prefix+"_BG", OBJPROP_BGCOLOR, C'20,20,20');
ObjectSetInteger(0, prefix+"_BG", OBJPROP_COLOR, C'60,60,60'); // Border
ObjectSetInteger(0, prefix+"_BG", OBJPROP_CORNER, CORNER_RIGHT_UPPER);
ObjectSetInteger(0, prefix+"_BG", OBJPROP_BACK, true);
// Draw Rows
DrawTextItem(prefix+"H1", "Execution Drag Model | " + _Symbol, baseX+pad, baseY+pad, clrWhite, 10, true);
DrawTextItem(prefix+"R1", "Notional Exposure:", baseX+pad, baseY+pad+(rowH*1), clrSilver);
DrawValItem(prefix+"V1", DoubleToString(notional, 2) + " " + ccy, baseX+w-pad, baseY+pad+(rowH*1), clrSilver);
DrawTextItem(prefix+"R2", "Broker Commissions:", baseX+pad, baseY+pad+(rowH*2), clrSilver);
DrawValItem(prefix+"V2", DoubleToString(comm, 2) + " " + ccy, baseX+w-pad, baseY+pad+(rowH*2), C'255,100,100');
DrawTextItem(prefix+"R3", liveSpread ? "Spread Drag (LIVE):" : "Spread Drag (STATIC):", baseX+pad, baseY+pad+(rowH*3), clrSilver);
DrawValItem(prefix+"V3", DoubleToString(spread, 2) + " " + ccy, baseX+w-pad, baseY+pad+(rowH*3), C'255,100,100');
DrawTextItem(prefix+"R4", "Est. Slippage (ATR):", baseX+pad, baseY+pad+(rowH*4), clrSilver);
DrawValItem(prefix+"V4", DoubleToString(slip, 2) + " " + ccy, baseX+w-pad, baseY+pad+(rowH*4), C'255,150,50');
DrawTextItem(prefix+"R5", "Total Overhead:", baseX+pad, baseY+pad+(rowH*5)+5, clrDodgerBlue, 9, true);
DrawValItem(prefix+"V5", DoubleToString(total, 2) + " " + ccy, baseX+w-pad, baseY+pad+(rowH*5)+5, clrDodgerBlue, 9, true);
DrawTextItem(prefix+"R6", "Portfolio Drag:", baseX+pad, baseY+pad+(rowH*6)+5, clrSilver);
DrawValItem(prefix+"V6", DoubleToString(drag, 3) + "%", baseX+w-pad, baseY+pad+(rowH*6)+5, clrSalmon, 9, true);
DrawTextItem(prefix+"R7", "Points to Breakeven:", baseX+pad, baseY+pad+(rowH*7)+5, clrGold, 9, true);
DrawValItem(prefix+"V7", DoubleToString(be_points, 1), baseX+w-pad, baseY+pad+(rowH*7)+5, clrGold, 9, true);
}
void DrawTextItem(string name, string text, int x, int y, color clr, int size=9, bool bold=false)
{
if(ObjectFind(0, name) < 0) ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
ObjectSetString(0, name, OBJPROP_TEXT, text);
ObjectSetString(0, name, OBJPROP_FONT, "Trebuchet MS");
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, size);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
}
void DrawValItem(string name, string text, int x, int y, color clr, int size=9, bool bold=false)
{
DrawTextItem(name, text, x, y, clr, size, bold);
ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_RIGHT_UPPER); // Right align the numbers
}