Math that changed how long I allow a scalp to live.
Commission is fixed per lot. Spread is paid up front. If my average winner needs three minutes to develop but my edge is really a thirty-second liquidity grab, I am financing hope with time while costs are already sunk.
Simple check I run per pair
1. Round-trip cost (spread + commission) in points
2. Median M1 noise over my session window
3. Typical time-to-+1R on my A setups from the last month of clean tickets
If cost is a large fraction of the first R and winners that take longer than X minutes have worse expectancy in the log, I shorten the time stop -- or I stop trading that pair at that size. Holding "because it might run" after the idea's urgency is gone is just turning a scalp into an accidental swing with retail costs.
I do not optimise hold time to the second. I want a boring band: idea pays or fails inside a window that matches how I entered.
Anyone else size hold-time rules off cost structure, or do you keep a fixed clock regardless of pair?¨
Hi LondonScalper,
You just articulated one of the hardest lessons in short-term trading: time is an active position against you when costs are sunk. Financing hope with time turns a structural edge (a fast liquidity sweep) into a coin flip with a negative expectancy.
To answer your question: the best short-term traders absolutely tie their time constraints to the asset's specific cost/volatility structure rather than using a universal fixed clock. A 3-minute hold on a highly liquid, low-spread pair during the London open is fundamentally different from a 3-minute hold on a wider-spread cross pair in the Asian session.
When your edge is a microstructure event (like an order block reaction or a liquidity grab), the "urgency" of the move is the edge itself. If the tape doesn't accelerate immediately, the premise of the trade is dead, even if the price hasn't hit your hard stop yet.
How commission per lot changes the optimal scalp hold time
-
LondonScalper
- Posts: 701
- Joined: Sat Sep 05, 2026 7:54 am
-
PropScalpDesk
- Posts: 273
- Joined: Sat Sep 19, 2026 7:50 pm
Re: How commission per lot changes the optimal scalp hold time
Commission fixes the clock on a scalp
This math changed my hold-time discipline too. Spread is paid up front; commission is paid per lot whether the idea needs thirty seconds or three minutes. If my real edge is a short liquidity grab, financing a longer hold is paying rent on hope.
Check I run per pair from Frankfurt:
I also refuse to “wait for breakeven” past the time stop. Breakeven hunting is often just unpaid holding after costs are sunk. Scratching on time is part of positive expectancy on short holds.
Do you set the time stop as a hard close, or as a management alert you still negotiate when the candle looks dramatic?
This math changed my hold-time discipline too. Spread is paid up front; commission is paid per lot whether the idea needs thirty seconds or three minutes. If my real edge is a short liquidity grab, financing a longer hold is paying rent on hope.
Check I run per pair from Frankfurt:
- Median time-to-target on winners last month
- Round-turn cost in points
- Time stop written before entry — scratch if structure is not working inside that window
I also refuse to “wait for breakeven” past the time stop. Breakeven hunting is often just unpaid holding after costs are sunk. Scratching on time is part of positive expectancy on short holds.
Do you set the time stop as a hard close, or as a management alert you still negotiate when the candle looks dramatic?
Re: How commission per lot changes the optimal scalp hold time
Here is a Pine Script v5 strategy template that implements your math. It includes a dashboard table that runs your "simple check per pair" (calculating the true cost ratio and M1 noise) and actively enforces a time-stop logic to kill trades that overstay their welcome.
Code: Select all
//@version=5
strategy("Scalp Time & Cost Constraint", overlay=true, margin_long=100, margin_short=100, calc_on_every_tick=true)
// ==============================================================================
// 1. INPUTS
// ==============================================================================
grp_cost = "Cost & Target Structure"
comm_per_lot = input.float(3.0, title="Round-Trip Commission ($/Lot)", group=grp_cost)
point_value = syminfo.pointvalue
target_1R = input.int(100, title="Typical 1R Target (in Points)", group=grp_cost)
max_ratio = input.float(15.0, title="Max Acceptable Cost/1R Ratio (%)", group=grp_cost)
grp_time = "Time Constraints"
max_hold_bars = input.int(3, title="Max Hold Time (Bars)", group=grp_time, tooltip="Kills trade if not at target by this bar count")
// ==============================================================================
// 2. MATH & METRICS (The "Simple Check")
// ==============================================================================
// A. Round-Trip Cost in Points
// syminfo.spread returns spread in ticks. We convert commission to points as well.
spread_pts = syminfo.spread
comm_pts = (comm_per_lot) / point_value // Simplified conversion for standard lots
total_cost = spread_pts + comm_pts
// B. Cost as a fraction of 1R
cost_ratio = (total_cost / target_1R) * 100
// C. M1 Noise (Using ATR as a proxy for median minute-by-minute noise)
m1_noise = request.security(syminfo.tickerid, "1", ta.atr(14)) / syminfo.mintick
// ==============================================================================
// 3. EXECUTION LOGIC (Demonstration)
// ==============================================================================
// For demonstration, a simple momentum crossover entry
longCond = ta.crossover(ta.sma(close, 5), ta.sma(close, 20))
// Only enter if the cost ratio makes mathematical sense
valid_environment = cost_ratio <= max_ratio
if longCond and strategy.position_size == 0 and valid_environment
strategy.entry("ScalpL", strategy.long)
// Track time in trade
var int entry_bar = na
if strategy.position_size != 0 and strategy.position_size[1] == 0
entry_bar := bar_index
bars_held = strategy.position_size != 0 ? (bar_index - entry_bar) : 0
time_stop_hit = bars_held >= max_hold_bars
// Exit Logic
target_price = strategy.position_avg_price + (target_1R * syminfo.mintick)
stop_price = strategy.position_avg_price - (target_1R * syminfo.mintick) // 1:1 risk
if strategy.position_size > 0
if time_stop_hit
strategy.close("ScalpL", comment="Time Stop (Hope Financed)")
else
strategy.exit("ExitL", "ScalpL", limit=target_price, stop=stop_price)
// ==============================================================================
// 4. DIAGNOSTIC DASHBOARD
// ==============================================================================
var table panel = table.new(position.top_right, 2, 4, bgcolor=color.new(color.black, 70), border_width=1, border_color=color.gray)
if barstate.islast
// Headers
table.cell(panel, 0, 0, "Scalp Viability Matrix", text_color=color.white, text_halign=text.align_left, text_size=size.small)
// Total Cost
table.cell(panel, 0, 1, "Total Cost (Spread+Comm):", text_color=color.silver, text_halign=text.align_left, text_size=size.small)
table.cell(panel, 1, 1, str.tostring(total_cost, "#.##") + " pts", text_color=color.white, text_halign=text.align_right, text_size=size.small)
// M1 Noise
table.cell(panel, 0, 2, "M1 Noise (ATR 14):", text_color=color.silver, text_halign=text.align_left, text_size=size.small)
table.cell(panel, 1, 2, str.tostring(m1_noise, "#.##") + " pts", text_color=color.white, text_halign=text.align_right, text_size=size.small)
// Cost Ratio
color ratio_col = cost_ratio > max_ratio ? color.red : color.green
table.cell(panel, 0, 3, "Cost vs 1R Ratio:", text_color=color.silver, text_halign=text.align_left, text_size=size.small)
table.cell(panel, 1, 3, str.tostring(cost_ratio, "#.##") + "%", text_color=ratio_col, text_halign=text.align_right, text_size=size.small)Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: How commission per lot changes the optimal scalp hold time
Why this approach works:
The Cost/1R Filter: If the current spread widens (common near rollover or high-impact news) and pushes the Cost vs 1R Ratio above your acceptable threshold (e.g., 15%), the script automatically invalidates the setup. You stop trading the pair at that size until costs normalize.
The Time Stop: The script actively counts the bars since the entry. If the setup doesn't pay out within your required window (e.g., 3 bars on an M1 chart), it ruthlessly cuts the trade at the market.
M1 Context: Pulling the M1 ATR dynamically via request.security keeps you grounded. If your 1R is 50 points, but M1 noise is only 5 points, a 3-minute time stop will practically never hit the target—allowing you to adjust your expectations or stay out.
The Cost/1R Filter: If the current spread widens (common near rollover or high-impact news) and pushes the Cost vs 1R Ratio above your acceptable threshold (e.g., 15%), the script automatically invalidates the setup. You stop trading the pair at that size until costs normalize.
The Time Stop: The script actively counts the bars since the entry. If the setup doesn't pay out within your required window (e.g., 3 bars on an M1 chart), it ruthlessly cuts the trade at the market.
M1 Context: Pulling the M1 ATR dynamically via request.security keeps you grounded. If your 1R is 50 points, but M1 noise is only 5 points, a 3-minute time stop will practically never hit the target—allowing you to adjust your expectations or stay out.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: How commission per lot changes the optimal scalp hold time
Here are the implementations for both MetaTrader 4 (MQL4) and MetaTrader 5 (MQL5).
Because indicators in MetaTrader cannot execute or close orders, both versions are built as trade-management Expert Advisors (EAs). They can run standalone to monitor manually opened tickets or be integrated directly into your existing execution scripts.
Each version:
1.) Calculates live round-trip cost (Spread + $/lot Commission converted into points via tick value).
2.) Measures M1 noise (ATR 14 in points).
3.) Evaluates the Cost vs. 1R ratio.
4.) Projects an on-chart diagnostic dashboard.
5.) Actively tracks open trades and executes a hard market close if elapsed time exceeds your hold threshold.
Because indicators in MetaTrader cannot execute or close orders, both versions are built as trade-management Expert Advisors (EAs). They can run standalone to monitor manually opened tickets or be integrated directly into your existing execution scripts.
Each version:
1.) Calculates live round-trip cost (Spread + $/lot Commission converted into points via tick value).
2.) Measures M1 noise (ATR 14 in points).
3.) Evaluates the Cost vs. 1R ratio.
4.) Projects an on-chart diagnostic dashboard.
5.) Actively tracks open trades and executes a hard market close if elapsed time exceeds your hold threshold.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: How commission per lot changes the optimal scalp hold time
1. MetaTrader 4 (MQL4)
Save this as an Expert Advisor (.mq4) in your MQL4/Experts directory:
Save this as an Expert Advisor (.mq4) in your MQL4/Experts directory:
Code: Select all
//+------------------------------------------------------------------+
//| ScalpTimeCostManager.mq4 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026"
#property link ""
#property version "1.00"
#property strict
//--- Inputs
input string sep0 = "=== Cost & Target Structure ===";
input double InpCommissionPerLot = 3.0; // Commission per Lot Round-Trip ($)
input int InpTarget1R = 100; // Typical 1R Target (Points)
input double InpMaxCostRatio = 15.0; // Max Acceptable Cost/1R Ratio (%)
input string sep1 = "=== Time Constraints ===";
input int InpMaxHoldSeconds = 180; // Max Hold Time (Seconds: 180 = 3m)
input bool InpEnforceTimeStop = true; // Actively Close Orders on Time Stop
input int InpMagicNumber = 0; // Magic Number to Manage (0 = Manual trades)
input int InpSlippage = 5; // Max Slippage (Points)
//--- Global Variables
int g_atrHandle = -1;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Comment("");
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. Math & Metrics
double spreadPoints = (double)MarketInfo(_Symbol, MODE_SPREAD);
// Convert $/lot commission into points
double tickValue = MarketInfo(_Symbol, MODE_TICKVALUE);
double tickSize = MarketInfo(_Symbol, MODE_TICKSIZE);
double pointVal = (tickSize > 0) ? (tickValue * (_Point / tickSize)) : tickValue;
double commPoints = 0.0;
if(pointVal > 0)
commPoints = InpCommissionPerLot / pointVal;
double totalCostPoints = spreadPoints + commPoints;
double costRatio = (InpTarget1R > 0) ? (totalCostPoints / (double)InpTarget1R) * 100.0 : 0.0;
// M1 Noise in points (14-period ATR)
double m1Atr = iATR(_Symbol, PERIOD_M1, 14, 0);
double m1NoisePoints = (_Point > 0) ? (m1Atr / _Point) : 0.0;
// 2. On-Chart Diagnostic Dashboard
string status = (costRatio <= InpMaxCostRatio) ? "VIABLE SETUP" : "COSTS TOO HIGH";
string dashboard = StringFormat(
"=== Scalp Viability Matrix [%s] ===\n" +
"Live Spread: %.1f pts\n" +
"Est. Commission: %.1f pts ($%.2f/lot)\n" +
"Total Sunk Cost: %.1f pts\n" +
"M1 Noise (ATR 14): %.1f pts\n" +
"Cost / 1R Ratio: %.2f%% (Max: %.1f%%)\n" +
"Status: %s\n" +
"Time Stop Limit: %d sec",
_Symbol, spreadPoints, commPoints, InpCommissionPerLot,
totalCostPoints, m1NoisePoints, costRatio, InpMaxCostRatio,
status, InpMaxHoldSeconds
);
Comment(dashboard);
// 3. Time Stop Management
if(InpEnforceTimeStop)
{
ManageTimeStops();
}
}
//+------------------------------------------------------------------+
//| Close trades that exceed hold duration |
//+------------------------------------------------------------------+
void ManageTimeStops()
{
datetime now = TimeCurrent();
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
continue;
if(OrderSymbol() != _Symbol)
continue;
if(InpMagicNumber != -1 && OrderMagicNumber() != InpMagicNumber)
continue;
int type = OrderType();
if(type != OP_BUY && type != OP_SELL)
continue;
datetime openTime = OrderOpenTime();
int elapsedSeconds = (int)(now - openTime);
if(elapsedSeconds >= InpMaxHoldSeconds)
{
double closePrice = (type == OP_BUY) ? Bid : Ask;
bool closed = OrderClose(OrderTicket(), OrderLots(), closePrice, InpSlippage, clrOrangeRed);
if(closed)
{
PrintFormat("Order #%d closed: Time stop exceeded (%d sec >= %d sec)",
OrderTicket(), elapsedSeconds, InpMaxHoldSeconds);
}
else
{
PrintFormat("Failed to close order #%d on time stop. Error: %d",
OrderTicket(), GetLastError());
}
}
}
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: How commission per lot changes the optimal scalp hold time
2. MetaTrader 5 (MQL5)
Save this as an Expert Advisor (.mq5) in your MQL5/Experts directory:
Save this as an Expert Advisor (.mq5) in your MQL5/Experts directory:
Code: Select all
//+------------------------------------------------------------------+
//| ScalpTimeCostManager.mq5 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026"
#property link ""
#property version "1.00"
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
//--- Inputs
input group "=== Cost & Target Structure ==="
input double InpCommissionPerLot = 3.0; // Commission per Lot Round-Trip ($)
input int InpTarget1R = 100; // Typical 1R Target (Points)
input double InpMaxCostRatio = 15.0; // Max Acceptable Cost/1R Ratio (%)
input group "=== Time Constraints ==="
input int InpMaxHoldSeconds = 180; // Max Hold Time (Seconds: 180 = 3m)
input bool InpEnforceTimeStop = true; // Actively Close Orders on Time Stop
input ulong InpMagicNumber = 0; // Magic Number (0 = Manual trades)
input ulong InpDeviation = 5; // Max Deviation/Slippage (Points)
//--- Global Objects
CTrade m_trade;
CPositionInfo m_position;
int m_atrHandle = INVALID_HANDLE;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
m_trade.SetExpertMagicNumber(InpMagicNumber);
m_trade.SetDeviationInPoints(InpDeviation);
m_atrHandle = iATR(_Symbol, PERIOD_M1, 14);
if(m_atrHandle == INVALID_HANDLE)
{
Print("Error initializing M1 ATR handle.");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(m_atrHandle != INVALID_HANDLE)
IndicatorRelease(m_atrHandle);
Comment("");
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. Math & Metrics
long spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
// Point value conversion
double pointVal = (tickSize > 0.0) ? (tickValue * (point / tickSize)) : tickValue;
double commPoints = 0.0;
if(pointVal > 0.0)
commPoints = InpCommissionPerLot / pointVal;
double totalCostPoints = (double)spread + commPoints;
double costRatio = (InpTarget1R > 0) ? (totalCostPoints / (double)InpTarget1R) * 100.0 : 0.0;
// M1 Noise (ATR 14 in points)
double atrBuf[];
ArraySetAsSeries(atrBuf, true);
double m1NoisePoints = 0.0;
if(CopyBuffer(m_atrHandle, 0, 0, 1, atrBuf) > 0 && point > 0.0)
{
m1NoisePoints = atrBuf[0] / point;
}
// 2. On-Chart Diagnostic Dashboard
string status = (costRatio <= InpMaxCostRatio) ? "VIABLE SETUP" : "COSTS TOO HIGH";
string dashboard = StringFormat(
"=== Scalp Viability Matrix [%s] ===\n" +
"Live Spread: %d pts\n" +
"Est. Commission: %.1f pts ($%.2f/lot)\n" +
"Total Sunk Cost: %.1f pts\n" +
"M1 Noise (ATR 14): %.1f pts\n" +
"Cost / 1R Ratio: %.2f%% (Max: %.1f%%)\n" +
"Status: %s\n" +
"Time Stop Limit: %d sec",
_Symbol, spread, commPoints, InpCommissionPerLot,
totalCostPoints, m1NoisePoints, costRatio, InpMaxCostRatio,
status, InpMaxHoldSeconds
);
Comment(dashboard);
// 3. Time Stop Management
if(InpEnforceTimeStop)
{
ManageTimeStops();
}
}
//+------------------------------------------------------------------+
//| Close positions exceeding max duration |
//+------------------------------------------------------------------+
void ManageTimeStops()
{
datetime now = TimeCurrent();
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(!m_position.SelectByIndex(i))
continue;
if(m_position.Symbol() != _Symbol)
continue;
if(InpMagicNumber != 0 && m_position.Magic() != InpMagicNumber)
continue;
datetime openTime = (datetime)m_position.Time();
int elapsedSeconds = (int)(now - openTime);
if(elapsedSeconds >= InpMaxHoldSeconds)
{
ulong ticket = m_position.Ticket();
if(m_trade.PositionClose(ticket))
{
PrintFormat("Position #%d closed: Time stop exceeded (%d sec >= %d sec)",
ticket, elapsedSeconds, InpMaxHoldSeconds);
}
else
{
PrintFormat("Failed to close position #%d on time stop. RetCode: %u",
ticket, m_trade.ResultRetcode());
}
}
}
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.