Advertisement IC Markets

How many pips a day is realistic for scalping

Discuss 1-minute to 15-minute price action setups, fading intraday momentum, key support/resistance zones, and proven short-term trading methodologies.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

How many pips a day is realistic for scalping

Post by PTScalper »

Hi guys,

what is your expectation? How many pips a day is realistic outcome for forex scalping strategy?

In my point of view, im happy, once i make at average around 3-5pips, on 80-120 scalps.
So lets say 240 - 600 pips.
Most of the time at beginning i use like positions 0.1lot .. so target profit is: 240 - 600$ a day.
Once i withdraw deposited money i scale more fastly, to positions 1 - 50 lots, there i can expect in best days profits like 300k$+
(It seems to be very big number, but i use exponential money management)
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Recommended broker for automated trading & scalping IC Markets
LondonScalper
Posts: 770
Joined: Sat Sep 05, 2026 7:54 am

Re: How many pips a day is realistic for scalping

Post by LondonScalper »

PTScalper wrote:im happy, once i make at average around 3-5pips, on 80-120 scalps. So lets say 240 - 600 pips.
Useful to separate gross pips from net expectancy after costs — they’re not the same conversation.

80–120 tickets a day at 3–5 pips average sounds like a high-frequency discretionary book. On majors that’s only realistic if all-in cost (spread + commission + typical slip) is a small fraction of the average win. If you’re paying ~0.6–1.0 pip round-turn all-in, a “3 pip average” can still be fine; if costs creep toward 1.5+, the same chart edge dies quietly.

I don’t target a pip quota. I target a small set of A+ windows (London open / early overlap) and accept that some days print 20–40 net pips and some days print near zero because I stood down. Forcing 240–600 “because the plan says so” is how ticket quality collapses after lunch.

The 1–50 lot scaling talk only makes sense after the per-ticket process is stable at small size. Curious what your average hold time is on those 80–120 — seconds, or a few minutes?
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: How many pips a day is realistic for scalping

Post by FTtrader »

LondonScalper wrote: Fri Sep 11, 2026 5:33 pm
PTScalper wrote:im happy, once i make at average around 3-5pips, on 80-120 scalps. So lets say 240 - 600 pips.
Useful to separate gross pips from net expectancy after costs — they’re not the same conversation.

80–120 tickets a day at 3–5 pips average sounds like a high-frequency discretionary book. On majors that’s only realistic if all-in cost (spread + commission + typical slip) is a small fraction of the average win. If you’re paying ~0.6–1.0 pip round-turn all-in, a “3 pip average” can still be fine; if costs creep toward 1.5+, the same chart edge dies quietly.

I don’t target a pip quota. I target a small set of A+ windows (London open / early overlap) and accept that some days print 20–40 net pips and some days print near zero because I stood down. Forcing 240–600 “because the plan says so” is how ticket quality collapses after lunch.

The 1–50 lot scaling talk only makes sense after the per-ticket process is stable at small size. Curious what your average hold time is on those 80–120 — seconds, or a few minutes?
Hello LondonScalper,

When you’re hunting for 3–5 pips, a 1.5 pip round-turn cost (spread + commission + slippage) means you're surrendering 30–50% of your gross edge right out of the gate. That is exactly why separating gross pips from net expectancy is a non-negotiable conversation for higher-frequency execution; the chart edge looks great until the broker takes their cut.

I completely align with your stance on targeting A+ windows rather than forcing a daily quota. My methodology relies strictly on raw price action and identifying liquidity sweeps mapped on the daily and 15-minute charts. The actual follow-through on those sweeps only reliably happens when the volume is there (London open / NY overlap). Forcing trades outside of those hours or relying on lagging indicators when liquidity dries up is a surefire way to bleed out your morning profits.

To answer your question regarding average hold times: I am firmly in the "minutes" camp. Because I am waiting for structural sweeps on the M15 and playing the immediate repricing, the market usually validates or invalidates the setup quickly. Holding for mere seconds puts you in direct competition with algorithmic HFTs, while holding for hours exposes the trade to broader intraday variance that falls outside the scope of a 3-5 pip target.

Below is a Pine Script I put together to visualize exactly what you're talking about—it drops a clean dashboard on the chart to strip away the gross illusion and show the true net expectancy per trade.
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: How many pips a day is realistic for scalping

Post by FTtrader »

Pine Script: Net Expectancy & Cost Dashboard

This indicator creates a dynamic on-chart table that calculates your true net expectancy based on your specific win rate, average targets, and broker costs. It translates commission into pips so you can see exactly how much of your edge is being eaten by fees.

Code: Select all

//@version=5
indicator("Net Expectancy & Cost Dashboard", overlay=true)

// =========================================================================
// INPUTS: Strategy Performance
// =========================================================================
winRate      = input.float(55.0, title="Win Rate (%)", group="Strategy Metrics") / 100
avgGrossWin  = input.float(4.0, title="Avg Gross Win (Pips)", group="Strategy Metrics")
avgGrossLoss = input.float(3.0, title="Avg Gross Loss (Pips)", group="Strategy Metrics")

// =========================================================================
// INPUTS: Broker Costs
// =========================================================================
commPerLot   = input.float(7.0, title="Round Turn Comm ($/Lot)", group="Execution Costs")
pipValue     = input.float(10.0, title="Pip Value ($/Lot)", tooltip="Standard lot typically $10", group="Execution Costs")
avgSpread    = input.float(0.8, title="Average Spread (Pips)", group="Execution Costs")
estSlippage  = input.float(0.2, title="Est. Slippage (Pips)", group="Execution Costs")

// =========================================================================
// CALCULATIONS
// =========================================================================
// Convert commission to a pip equivalent
commInPips   = commPerLot / pipValue
allInCost    = commInPips + avgSpread + estSlippage

// Net outcomes per trade
netWin       = avgGrossWin - allInCost
netLoss      = avgGrossLoss + allInCost

// Expectancy Math
grossExpectancy = (avgGrossWin * winRate) - (avgGrossLoss * (1 - winRate))
netExpectancy   = (netWin * winRate) - (netLoss * (1 - winRate))

// =========================================================================
// DASHBOARD UI (TABLE)
// =========================================================================
var table expTable = table.new(position.bottom_right, 2, 5, bgcolor=color.new(color.black, 10), border_width=1, border_color=color.new(color.gray, 50))

if barstate.islast
    // Headers
    table.cell(expTable, 0, 0, "METRIC", text_color=color.white, text_halign=text.align_left, bgcolor=color.new(color.gray, 80))
    table.cell(expTable, 1, 0, "VALUE (PIPS)", text_color=color.white, text_halign=text.align_right, bgcolor=color.new(color.gray, 80))

    // All-In Cost
    table.cell(expTable, 0, 1, "All-In Cost / Trade", text_color=color.white, text_halign=text.align_left)
    table.cell(expTable, 1, 1, str.tostring(allInCost, "#.##"), text_color=color.red, text_halign=text.align_right)

    // Gross Expectancy
    table.cell(expTable, 0, 2, "Gross Expectancy", text_color=color.white, text_halign=text.align_left)
    table.cell(expTable, 1, 2, str.tostring(grossExpectancy, "#.##"), text_color=color.gray, text_halign=text.align_right)

    // Net Expectancy
    color netColor = netExpectancy > 0 ? color.lime : color.red
    table.cell(expTable, 0, 3, "True Net Expectancy", text_color=color.white, text_halign=text.align_left)
    table.cell(expTable, 1, 3, str.tostring(netExpectancy, "#.##"), text_color=netColor, text_halign=text.align_right)
    
    // 100-Trade Projection
    table.cell(expTable, 0, 4, "Net / 100 Tickets", text_color=color.white, text_halign=text.align_left)
    table.cell(expTable, 1, 4, str.tostring(netExpectancy * 100, "#.##"), text_color=netColor, text_halign=text.align_right)
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: How many pips a day is realistic for scalping

Post by FTtrader »

How to use it:

Strategy Metrics: Plug in your historical gross averages.

Execution Costs: Input your specific tier's commission structure and the average spread of the asset you are trading.

The Output: It prints a table in the bottom right corner showing exactly what your net expectancy is per ticket, and projects your total net yield over a 100-trade sample size.

Do you want me to modify this script to pull the live, dynamic spread directly from the broker feed, or do you prefer keeping the costs fixed as inputs to establish a consistent baseline?
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: How many pips a day is realistic for scalping

Post by FTtrader »

Regarding your question on hold times: my average duration is in the minutes. The strategy is designed to capitalize on immediate structural repricing following an M15 sweep. Holding for mere seconds introduces direct latency competition with HFT models, while stretching trades into hours introduces intraday macro variance that falls completely outside the statistical parameters of a sub-5-pip target.

To quantify exactly what you mentioned regarding costs, I wrote a Pine Script that projects true net expectancy. It strips away the gross illusion and calculates the actual per-ticket yield after accounting for spread, commission, and estimated slippage.

Pine Script: Net Expectancy & Friction Model

This updated version uses more rigorous quantitative parameters and a cleaner, institutional dashboard to model your exact execution drag.

Code: Select all

//@version=5
indicator("Net Expectancy & Friction Model", overlay=true)

// =========================================================================
// INPUTS: Statistical Averages
// =========================================================================
winRate       = input.float(55.0, title="Historical Win Rate (%)", group="Strategy Metrics") / 100
meanGrossWin  = input.float(4.0, title="Mean Gross Win (Pips)", group="Strategy Metrics")
meanGrossLoss = input.float(3.0, title="Mean Gross Loss (Pips)", group="Strategy Metrics")

// =========================================================================
// INPUTS: Execution Friction
// =========================================================================
commPerLot    = input.float(7.0, title="Round Turn Comm ($/Lot)", group="Execution Friction")
pipValue      = input.float(10.0, title="Pip Value ($/Lot)", group="Execution Friction")
avgSpread     = input.float(0.8, title="Mean Spread (Pips)", group="Execution Friction")
estSlippage   = input.float(0.2, title="Mean Slippage (Pips)", group="Execution Friction")

// =========================================================================
// EXPECTANCY MATH
// =========================================================================
// Calculate total friction in pips
commInPips    = commPerLot / pipValue
totalFriction = commInPips + avgSpread + estSlippage

// Calculate net distributions
netWinMean    = meanGrossWin - totalFriction
netLossMean   = meanGrossLoss + totalFriction

// Calculate Expectancy
grossExp      = (meanGrossWin * winRate) - (meanGrossLoss * (1 - winRate))
netExp        = (netWinMean * winRate) - (netLossMean * (1 - winRate))

// =========================================================================
// DASHBOARD UI
// =========================================================================
var table expTable = table.new(position.bottom_right, 2, 5, bgcolor=color.rgb(15, 15, 15, 10), border_width=1, border_color=color.rgb(60, 60, 60))

if barstate.islast
    // Headers
    table.cell(expTable, 0, 0, "METRIC", text_color=color.gray, text_halign=text.align_left, text_size=size.small)
    table.cell(expTable, 1, 0, "VALUE (PIPS)", text_color=color.gray, text_halign=text.align_right, text_size=size.small)

    // Execution Friction
    table.cell(expTable, 0, 1, "Total Execution Friction", text_color=color.white, text_halign=text.align_left, text_size=size.small)
    table.cell(expTable, 1, 1, str.tostring(totalFriction, "#.##"), text_color=color.red, text_halign=text.align_right, text_size=size.small)

    // Gross Expectancy
    table.cell(expTable, 0, 2, "Gross Expectancy (Pre-Cost)", text_color=color.white, text_halign=text.align_left, text_size=size.small)
    table.cell(expTable, 1, 2, str.tostring(grossExp, "#.##"), text_color=color.gray, text_halign=text.align_right, text_size=size.small)

    // Net Expectancy
    color netColor = netExp > 0 ? color.lime : color.red
    table.cell(expTable, 0, 3, "Net Expectancy (True Yield)", text_color=color.white, text_halign=text.align_left, text_size=size.small)
    table.cell(expTable, 1, 3, str.tostring(netExp, "#.##"), text_color=netColor, text_halign=text.align_right, text_size=size.small)
    
    // N=100 Projection
    table.cell(expTable, 0, 4, "Projected Yield (N=100)", text_color=color.white, text_halign=text.align_left, text_size=size.small)
    table.cell(expTable, 1, 4, str.tostring(netExp * 100, "#.##"), text_color=netColor, text_halign=text.align_right, text_size=size.small)
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: How many pips a day is realistic for scalping

Post by FTtrader »

To pull the live, dynamic spread directly from your broker's feed, we need to upgrade the script to Pine Script v6. This version introduced native ask and bid variables, allowing you to bypass static inputs and calculate the real-time spread instantly.

Because ask and bid are tick-level metrics, we have to request them specifically from the 1-tick ("1T") timeframe using request.security(). This ensures the live feed calculates accurately even if your primary structural analysis is on the M15 or H1 charts.

Here is the upgraded script:

Code: Select all

//@version=6
indicator("Net Expectancy & Friction Model (Live Spread)", overlay=true)

// =========================================================================
// INPUTS: Statistical Averages
// =========================================================================
winRate       = input.float(55.0, title="Historical Win Rate (%)", group="Strategy Metrics") / 100
meanGrossWin  = input.float(4.0, title="Mean Gross Win (Pips)", group="Strategy Metrics")
meanGrossLoss = input.float(3.0, title="Mean Gross Loss (Pips)", group="Strategy Metrics")

// =========================================================================
// INPUTS: Execution Friction
// =========================================================================
commPerLot    = input.float(7.0, title="Round Turn Comm ($/Lot)", group="Execution Costs")
pipValue      = input.float(10.0, title="Pip Value ($/Lot)", group="Execution Costs")
estSlippage   = input.float(0.2, title="Mean Slippage (Pips)", group="Execution Costs")
fallbackSp    = input.float(0.8, title="Historical Fallback Spread", tooltip="Live spread is only available on real-time bars. This value is used for historical bars.", group="Execution Costs")

// =========================================================================
// LIVE SPREAD CALCULATION
// =========================================================================
// Fetch live Ask and Bid from the 1-tick timeframe
liveAsk = request.security(syminfo.tickerid, "1T", ask)
liveBid = request.security(syminfo.tickerid, "1T", bid)

// Determine pip size (Forex pairs usually have a pip size of mintick * 10, e.g., 0.0001. Indices/Metals use mintick * 1)
pipSize = syminfo.type == "forex" ? syminfo.mintick * 10 : syminfo.mintick

// Calculate live spread in pips. If na (historical bar), use fallback.
liveSpreadPips = (liveAsk - liveBid) / pipSize
activeSpread   = not na(liveSpreadPips) ? liveSpreadPips : fallbackSp

// =========================================================================
// EXPECTANCY MATH
// =========================================================================
// Calculate total friction in pips
commInPips    = commPerLot / pipValue
totalFriction = commInPips + activeSpread + estSlippage

// Calculate net distributions
netWinMean    = meanGrossWin - totalFriction
netLossMean   = meanGrossLoss + totalFriction

// Calculate Expectancy
grossExp      = (meanGrossWin * winRate) - (meanGrossLoss * (1 - winRate))
netExp        = (netWinMean * winRate) - (netLossMean * (1 - winRate))

// =========================================================================
// DASHBOARD UI
// =========================================================================
var table expTable = table.new(position.bottom_right, 2, 6, bgcolor=color.rgb(15, 15, 15, 10), border_width=1, border_color=color.rgb(60, 60, 60))

if barstate.islast
    // Headers
    table.cell(expTable, 0, 0, "METRIC", text_color=color.gray, text_halign=text.align_left, text_size=size.small)
    table.cell(expTable, 1, 0, "VALUE (PIPS)", text_color=color.gray, text_halign=text.align_right, text_size=size.small)

    // Current Active Spread
    spreadColor = not na(liveSpreadPips) ? color.aqua : color.gray
    table.cell(expTable, 0, 1, "Live Spread Feed", text_color=color.white, text_halign=text.align_left, text_size=size.small)
    table.cell(expTable, 1, 1, str.tostring(activeSpread, "#.##"), text_color=spreadColor, text_halign=text.align_right, text_size=size.small)

    // Execution Friction
    table.cell(expTable, 0, 2, "Total Execution Friction", text_color=color.white, text_halign=text.align_left, text_size=size.small)
    table.cell(expTable, 1, 2, str.tostring(totalFriction, "#.##"), text_color=color.red, text_halign=text.align_right, text_size=size.small)

    // Gross Expectancy
    table.cell(expTable, 0, 3, "Gross Expectancy (Pre-Cost)", text_color=color.white, text_halign=text.align_left, text_size=size.small)
    table.cell(expTable, 1, 3, str.tostring(grossExp, "#.##"), text_color=color.gray, text_halign=text.align_right, text_size=size.small)

    // Net Expectancy
    color netColor = netExp > 0 ? color.lime : color.red
    table.cell(expTable, 0, 4, "Net Expectancy (True Yield)", text_color=color.white, text_halign=text.align_left, text_size=size.small)
    table.cell(expTable, 1, 4, str.tostring(netExp, "#.##"), text_color=netColor, text_halign=text.align_right, text_size=size.small)
    
    // N=100 Projection
    table.cell(expTable, 0, 5, "Projected Yield (N=100)", text_color=color.white, text_halign=text.align_left, text_size=size.small)
    table.cell(expTable, 1, 5, str.tostring(netExp * 100, "#.##"), text_color=netColor, text_halign=text.align_right, text_size=size.small)
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: How many pips a day is realistic for scalping

Post by FTtrader »

Key Technical Adjustments

Version Upgrade (//@version=6): Unlocks the native tick-level bid/ask objects required for this data. Tick-Level Targeting: The request.security(..., "1T", ...) parameters guarantee the script pulls the actual raw quotes and doesn't just return the current bar's close price.

Asset-Agnostic Pip Math: The script evaluates syminfo.type to automatically determine if you are charting a Forex pair or an index/metal, scaling the ticks into pips appropriately.

Historical Fallback Mechanism: Live bid/ask arrays are only generated in real-time. If you load this script on historical bars (where live data prints as na), it will automatically insert the fallbackSp so the math doesn't break during backtesting. The dashboard will render the spread in blue when the live feed is active, and drop to gray when it's relying on historical fallback data.
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: How many pips a day is realistic for scalping

Post by FTtrader »

Here are the translations for both MetaTrader 4 and MetaTrader 5.

Since MetaTrader does not have a native "table" object like Pine Script, these indicators utilize a series of dynamically rendered OBJ_LABEL components anchored to the bottom right of the chart (CORNER_RIGHT_LOWER).

Both scripts automatically detect 3-digit and 5-digit brokers to normalize the spread and tick math into standard pips.

MQL4: Net Expectancy & Friction Model

Save this as a .mq4 file in your MQL4\Indicators folder.

Code: Select all

//+------------------------------------------------------------------+
//|                                        NetExpectancyFriction.mq4 |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property strict

// =========================================================================
// INPUTS
// =========================================================================
input string   sep1 = "--- Strategy Metrics ---";
input double   WinRate = 55.0;          // Historical Win Rate (%)
input double   MeanGrossWin = 4.0;      // Mean Gross Win (Pips)
input double   MeanGrossLoss = 3.0;     // Mean Gross Loss (Pips)

input string   sep2 = "--- Execution Friction ---";
input double   CommPerLot = 7.0;        // Round Turn Comm ($/Lot)
input double   PipValue = 10.0;         // Pip Value ($/Lot)
input double   EstSlippage = 0.2;       // Mean Slippage (Pips)

// Globals
double pipSize;
string objPrefix = "ExpDash_";

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit() {
    // Normalize pip size for 3/5 digit brokers
    pipSize = Point;
    if (Digits == 3 || Digits == 5) pipSize *= 10.0;
    
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
    ObjectsDeleteAll(0, objPrefix);
}

//+------------------------------------------------------------------+
//| 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[]) 
{
    // Live Spread Calculation
    double liveSpreadPips = (Ask - Bid) / pipSize;
    
    // Expectancy Math
    double wr = WinRate / 100.0;
    double commInPips = CommPerLot / PipValue;
    double totalFriction = commInPips + liveSpreadPips + EstSlippage;
    
    double netWinMean = MeanGrossWin - totalFriction;
    double netLossMean = MeanGrossLoss + totalFriction;
    
    double grossExp = (MeanGrossWin * wr) - (MeanGrossLoss * (1.0 - wr));
    double netExp = (netWinMean * wr) - (netLossMean * (1.0 - wr));
    double projectedYield = netExp * 100.0;
    
    // Render Dashboard
    color netColor = (netExp > 0) ? clrLime : clrRed;
    
    DrawRow(1, "Live Spread Feed", DoubleToStr(liveSpreadPips, 2), clrAqua);
    DrawRow(2, "Total Execution Friction", DoubleToStr(totalFriction, 2), clrRed);
    DrawRow(3, "Gross Expectancy (Pre-Cost)", DoubleToStr(grossExp, 2), clrSilver);
    DrawRow(4, "Net Expectancy (True Yield)", DoubleToStr(netExp, 2), netColor);
    DrawRow(5, "Projected Yield (N=100)", DoubleToStr(projectedYield, 2), netColor);

    return(rates_total);
}

//+------------------------------------------------------------------+
//| Helper: Draw Dashboard Row                                       |
//+------------------------------------------------------------------+
void DrawRow(int rowNumber, string label, string val, color valColor) {
    int yOffset = rowNumber * 20 + 10;
    
    string labelName = objPrefix + "lbl_" + IntegerToString(rowNumber);
    string valName = objPrefix + "val_" + IntegerToString(rowNumber);
    
    // Create or Update Label
    if(ObjectFind(0, labelName) < 0) {
        ObjectCreate(0, labelName, OBJ_LABEL, 0, 0, 0);
        ObjectSetInteger(0, labelName, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
        ObjectSetInteger(0, labelName, OBJPROP_ANCHOR, ANCHOR_RIGHT_LOWER);
        ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, 150);
        ObjectSetString(0, labelName, OBJPROP_FONT, "Trebuchet MS");
        ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 9);
        ObjectSetInteger(0, labelName, OBJPROP_COLOR, clrWhite);
    }
    ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, yOffset);
    ObjectSetString(0, labelName, OBJPROP_TEXT, label + ":");

    // Create or Update Value
    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, 10);
        ObjectSetString(0, valName, OBJPROP_FONT, "Trebuchet MS");
        ObjectSetInteger(0, valName, OBJPROP_FONTSIZE, 9);
    }
    ObjectSetInteger(0, valName, OBJPROP_YDISTANCE, yOffset);
    ObjectSetInteger(0, valName, OBJPROP_COLOR, valColor);
    ObjectSetString(0, valName, OBJPROP_TEXT, val);
}
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: How many pips a day is realistic for scalping

Post by FTtrader »

MQL5: Net Expectancy & Friction Model

Save this as a .mq5 file in your MQL5\Indicators folder. The MQL5 version uses SymbolInfoDouble() to fetch the raw tick data seamlessly.

Code: Select all

//+------------------------------------------------------------------+
//|                                        NetExpectancyFriction.mq5 |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_plots 0

// =========================================================================
// INPUTS
// =========================================================================
input string   sep1 = "--- Strategy Metrics ---";
input double   WinRate = 55.0;          // Historical Win Rate (%)
input double   MeanGrossWin = 4.0;      // Mean Gross Win (Pips)
input double   MeanGrossLoss = 3.0;     // Mean Gross Loss (Pips)

input string   sep2 = "--- Execution Friction ---";
input double   CommPerLot = 7.0;        // Round Turn Comm ($/Lot)
input double   PipValue = 10.0;         // Pip Value ($/Lot)
input double   EstSlippage = 0.2;       // Mean Slippage (Pips)

// Globals
double pipSize;
string objPrefix = "ExpDash_";

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit() {
    // Normalize pip size for 3/5 digit brokers
    double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
    int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
    
    pipSize = point;
    if (digits == 3 || digits == 5) pipSize *= 10.0;
    
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
    ObjectsDeleteAll(0, objPrefix);
    ChartRedraw();
}

//+------------------------------------------------------------------+
//| 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[]) 
{
    // Fetch Live Tick Data
    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    
    // Live Spread Calculation
    double liveSpreadPips = (ask - bid) / pipSize;
    
    // Expectancy Math
    double wr = WinRate / 100.0;
    double commInPips = CommPerLot / PipValue;
    double totalFriction = commInPips + liveSpreadPips + EstSlippage;
    
    double netWinMean = MeanGrossWin - totalFriction;
    double netLossMean = MeanGrossLoss + totalFriction;
    
    double grossExp = (MeanGrossWin * wr) - (MeanGrossLoss * (1.0 - wr));
    double netExp = (netWinMean * wr) - (netLossMean * (1.0 - wr));
    double projectedYield = netExp * 100.0;
    
    // Render Dashboard
    color netColor = (netExp > 0) ? clrLime : clrRed;
    
    DrawRow(1, "Live Spread Feed", DoubleToString(liveSpreadPips, 2), clrAqua);
    DrawRow(2, "Total Execution Friction", DoubleToString(totalFriction, 2), clrRed);
    DrawRow(3, "Gross Expectancy (Pre-Cost)", DoubleToString(grossExp, 2), clrSilver);
    DrawRow(4, "Net Expectancy (True Yield)", DoubleToString(netExp, 2), netColor);
    DrawRow(5, "Projected Yield (N=100)", DoubleToString(projectedYield, 2), netColor);

    return(rates_total);
}

//+------------------------------------------------------------------+
//| Helper: Draw Dashboard Row                                       |
//+------------------------------------------------------------------+
void DrawRow(int rowNumber, string label, string val, color valColor) {
    int yOffset = rowNumber * 20 + 10;
    
    string labelName = objPrefix + "lbl_" + IntegerToString(rowNumber);
    string valName = objPrefix + "val_" + IntegerToString(rowNumber);
    
    // Create or Update Label
    if(ObjectFind(0, labelName) < 0) {
        ObjectCreate(0, labelName, OBJ_LABEL, 0, 0, 0);
        ObjectSetInteger(0, labelName, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
        ObjectSetInteger(0, labelName, OBJPROP_ANCHOR, ANCHOR_RIGHT_LOWER);
        ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, 150);
        ObjectSetString(0, labelName, OBJPROP_FONT, "Trebuchet MS");
        ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 9);
        ObjectSetInteger(0, labelName, OBJPROP_COLOR, clrWhite);
    }
    ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, yOffset);
    ObjectSetString(0, labelName, OBJPROP_TEXT, label + ":");

    // Create or Update Value
    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, 10);
        ObjectSetString(0, valName, OBJPROP_FONT, "Trebuchet MS");
        ObjectSetInteger(0, valName, OBJPROP_FONTSIZE, 9);
    }
    ObjectSetInteger(0, valName, OBJPROP_YDISTANCE, yOffset);
    ObjectSetInteger(0, valName, OBJPROP_COLOR, valColor);
    ObjectSetString(0, valName, OBJPROP_TEXT, val);
}
Post Reply