Page 2 of 3
Re: Why most M1 indicators fail after transaction costs
Posted: Fri Sep 18, 2026 7:00 pm
by PTScalper
1. MQL4 Implementation (MetaTrader 4)
Save this file as M1_CostAudit.mq4 in your MQL4\Experts directory.
Code: Select all
//+------------------------------------------------------------------+
//| M1_CostAudit.mq4 |
//| Quantitative Prototyping |
//+------------------------------------------------------------------+
#property copyright "Quantitative Insights"
#property link ""
#property version "1.00"
#property strict
//--- Input Parameters
input string Grp1 = "--- Primary Trigger Logic ---";
input int FastEMA = 9;
input int SlowEMA = 21;
input string Grp2 = "--- Execution Filters ---";
input bool UseHTFFilter = true;
input bool UseATRFilter = true;
input double MinATRPips = 3.0;
input string Grp3 = "--- Risk Management ---";
input double LotSize = 0.1;
input int MagicNumber = 100100;
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Calculate Pip Size based on broker digit format
double pipSize = Point;
if(Digits == 3 || Digits == 5) pipSize = Point * 10;
// 1. Fetch Indicator Data
double fast0 = iMA(Symbol(), PERIOD_M1, FastEMA, 0, MODE_EMA, PRICE_CLOSE, 0);
double fast1 = iMA(Symbol(), PERIOD_M1, FastEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
double slow0 = iMA(Symbol(), PERIOD_M1, SlowEMA, 0, MODE_EMA, PRICE_CLOSE, 0);
double slow1 = iMA(Symbol(), PERIOD_M1, SlowEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
double htfEMA = iMA(Symbol(), PERIOD_M15, 200, 0, MODE_EMA, PRICE_CLOSE, 0);
double atr = iATR(Symbol(), PERIOD_M1, 14, 0);
// 2. Logic Compilation
bool longCross = (fast0 > slow0 && fast1 <= slow1);
bool shortCross = (fast0 < slow0 && fast1 >= slow1);
bool htfBullish = (Close[0] > htfEMA);
bool htfBearish = (Close[0] < htfEMA);
double atrInPips = atr / pipSize;
bool volatilitySufficient = (atrInPips >= MinATRPips);
bool validLongEnv = (!UseHTFFilter || htfBullish) && (!UseATRFilter || volatilitySufficient);
bool validShortEnv = (!UseHTFFilter || htfBearish) && (!UseATRFilter || volatilitySufficient);
// 3. Execution & Position Management
int openPositions = 0;
int orderType = -1;
int ticket = -1;
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
openPositions++;
orderType = OrderType();
ticket = OrderTicket();
}
}
}
// Exit Logic (Reversion Cross)
if(openPositions > 0)
{
if(orderType == OP_BUY && shortCross)
bool closed = OrderClose(ticket, OrderLots(), Bid, 3, clrRed);
if(orderType == OP_SELL && longCross)
bool closed = OrderClose(ticket, OrderLots(), Ask, 3, clrBlue);
}
// Entry Logic
if(openPositions == 0)
{
if(longCross && validLongEnv)
int res = OrderSend(Symbol(), OP_BUY, LotSize, Ask, 3, 0, 0, "M1_Long", MagicNumber, 0, clrBlue);
if(shortCross && validShortEnv)
int res = OrderSend(Symbol(), OP_SELL, LotSize, Bid, 3, 0, 0, "M1_Short", MagicNumber, 0, clrRed);
}
}
Re: Why most M1 indicators fail after transaction costs
Posted: Fri Sep 18, 2026 7:00 pm
by PTScalper
2. MQL5 Implementation (MetaTrader 5)
MQL5 is architecturally superior for this audit due to its precise tick modeling and integrated commission handling in the strategy tester. Save this as M1_CostAudit.mq5 in your MQL5\Experts directory.
Code: Select all
//+------------------------------------------------------------------+
//| M1_CostAudit.mq5 |
//| Quantitative Prototyping |
//+------------------------------------------------------------------+
#property copyright "Quantitative Insights"
#property link ""
#property version "1.00"
#include <Trade\Trade.mqh>
CTrade trade;
//--- Input Parameters
input group "--- Primary Trigger Logic ---"
input int FastEMA = 9;
input int SlowEMA = 21;
input group "--- Execution Filters ---"
input bool UseHTFFilter = true;
input bool UseATRFilter = true;
input double MinATRPips = 3.0;
input group "--- Risk Management ---"
input double LotSize = 0.1;
input ulong MagicNumber = 100100;
//--- Indicator Handles
int handleFast, handleSlow, handleHTF, handleATR;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
trade.SetExpertMagicNumber(MagicNumber);
handleFast = iMA(_Symbol, PERIOD_M1, FastEMA, 0, MODE_EMA, PRICE_CLOSE);
handleSlow = iMA(_Symbol, PERIOD_M1, SlowEMA, 0, MODE_EMA, PRICE_CLOSE);
handleHTF = iMA(_Symbol, PERIOD_M15, 200, 0, MODE_EMA, PRICE_CLOSE);
handleATR = iATR(_Symbol, PERIOD_M1, 14);
if(handleFast == INVALID_HANDLE || handleSlow == INVALID_HANDLE || handleHTF == INVALID_HANDLE || handleATR == INVALID_HANDLE)
{
Print("Error initializing indicator handles.");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
double pipSize = _Point;
if(_Digits == 3 || _Digits == 5) pipSize = _Point * 10;
// 1. Fetch Indicator Data Arrays
double fast[], slow[], htf[], atr[], close[];
ArraySetAsSeries(fast, true); ArraySetAsSeries(slow, true);
ArraySetAsSeries(htf, true); ArraySetAsSeries(atr, true); ArraySetAsSeries(close, true);
if(CopyBuffer(handleFast, 0, 0, 2, fast) <= 0) return;
if(CopyBuffer(handleSlow, 0, 0, 2, slow) <= 0) return;
if(CopyBuffer(handleHTF, 0, 0, 1, htf) <= 0) return;
if(CopyBuffer(handleATR, 0, 0, 1, atr) <= 0) return;
if(CopyRates(_Symbol, PERIOD_M1, 0, 1, close) <= 0) return; // Note: CopyRates requires MqlRates, simplified to CopyClose here
double currentClose[];
ArraySetAsSeries(currentClose, true);
CopyClose(_Symbol, PERIOD_M1, 0, 1, currentClose);
// 2. Logic Compilation
bool longCross = (fast[0] > slow[0] && fast[1] <= slow[1]);
bool shortCross = (fast[0] < slow[0] && fast[1] >= slow[1]);
bool htfBullish = (currentClose[0] > htf[0]);
bool htfBearish = (currentClose[0] < htf[0]);
double atrInPips = atr[0] / pipSize;
bool volatilitySufficient = (atrInPips >= MinATRPips);
bool validLongEnv = (!UseHTFFilter || htfBullish) && (!UseATRFilter || volatilitySufficient);
bool validShortEnv = (!UseHTFFilter || htfBearish) && (!UseATRFilter || volatilitySufficient);
// 3. Execution & Position Management
bool hasOpenPosition = PositionSelect(_Symbol);
long posType = PositionGetInteger(POSITION_TYPE);
// Exit Logic (Reversion Cross)
if(hasOpenPosition)
{
if(posType == POSITION_TYPE_BUY && shortCross)
trade.PositionClose(_Symbol);
if(posType == POSITION_TYPE_SELL && longCross)
trade.PositionClose(_Symbol);
}
// Entry Logic
if(!PositionSelect(_Symbol))
{
if(longCross && validLongEnv)
trade.Buy(LotSize, _Symbol);
if(shortCross && validShortEnv)
trade.Sell(LotSize, _Symbol);
}
}
Re: Why most M1 indicators fail after transaction costs
Posted: Fri Sep 18, 2026 7:01 pm
by PTScalper
How to Conduct the Quantitative Audit in MT5
To accurately replicate the cost degradation observed in the theory:
1.) Open the MT5 Strategy Tester (Ctrl + R).
2.) Select the M1_CostAudit.ex5 Expert Advisor.
3.) Select an instrument with high liquidity (e.g., EURUSD).
4.) Critical Step: In the tester settings, set Delay to Random or 50-100ms (to simulate execution slippage). Set Modeling to Every tick based on real ticks.
5.) Under the symbol properties or execution settings in MT5, ensure your broker's actual commission structure is applied (usually $3-$4 per side per lot).
6.) Run the test with all filters false. Observe the gross vs. net discrepancy.
7.) Run the test with filters true. You will observe execution frequency drop, but the average EV per trade will stabilize.
Re: Why most M1 indicators fail after transaction costs
Posted: Fri Sep 18, 2026 7:02 pm
by PTScalper
Porting this to cTrader’s C# API (cAlgo) is where the cost-audit becomes highly precise. Because cTrader natively supports C# and object-oriented architecture, we can handle the multi-timeframe (MTF) logic much more cleanly than MQL's buffer arrays.
To address the "repaint / recalc that vanishes live" issue you mentioned in the original autopsy, this cBot uses OnBar() rather than OnTick() and evaluates the EMA crosses on the first closed index (Last(1)). This ensures the backtest strictly reflects confirmed signals, preventing the illusion of intra-bar profitability.
cTrader Implementation (cAlgo)
Save this as a new cBot in the cTrader Automate tab.
Code: Select all
using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class M1CostAudit : Robot
{
[Parameter("Fast EMA", Group = "Primary Trigger", DefaultValue = 9)]
public int FastEmaLength { get; set; }
[Parameter("Slow EMA", Group = "Primary Trigger", DefaultValue = 21)]
public int SlowEmaLength { get; set; }
[Parameter("Use 15m Trend Filter", Group = "Execution Filters", DefaultValue = true)]
public bool UseHtfFilter { get; set; }
[Parameter("Use ATR Volatility Floor", Group = "Execution Filters", DefaultValue = true)]
public bool UseAtrFilter { get; set; }
[Parameter("Min ATR (Pips)", Group = "Execution Filters", DefaultValue = 3.0)]
public double MinAtrPips { get; set; }
[Parameter("Volume (Units)", Group = "Risk Management", DefaultValue = 100000)]
public double VolumeInUnits { get; set; }
private ExponentialMovingAverage _fastEma;
private ExponentialMovingAverage _slowEma;
private ExponentialMovingAverage _htfEma;
private AverageTrueRange _atr;
private Bars _m15Bars;
protected override void OnStart()
{
// Initialize M1 Indicators
_fastEma = Indicators.ExponentialMovingAverage(Bars.ClosePrices, FastEmaLength);
_slowEma = Indicators.ExponentialMovingAverage(Bars.ClosePrices, SlowEmaLength);
// ATR configured for Simple MA type as is standard
_atr = Indicators.AverageTrueRange(Bars, 14, MovingAverageType.Simple);
// Initialize MTF (15-Minute) Indicators
_m15Bars = MarketData.GetBars(TimeFrame.Minute15);
_htfEma = Indicators.ExponentialMovingAverage(_m15Bars.ClosePrices, 200);
}
protected override void OnBar()
{
// 1. Trigger Logic: Evaluated on closed bars (index 1) to prevent live repainting
bool isLongCross = _fastEma.Result.HasCrossedAbove(_slowEma.Result, 1);
bool isShortCross = _fastEma.Result.HasCrossedBelow(_slowEma.Result, 1);
if (!isLongCross && !isShortCross) return;
// 2. Filter Evaluation
bool validLongEnv = true;
bool validShortEnv = true;
if (UseHtfFilter)
{
// Retrieve the most recently closed 15m bar to avoid look-ahead bias
double currentHtfClose = _m15Bars.ClosePrices.Last(1);
double currentHtfEma = _htfEma.Result.Last(1);
bool htfBullish = currentHtfClose > currentHtfEma;
bool htfBearish = currentHtfClose < currentHtfEma;
validLongEnv &= htfBullish;
validShortEnv &= htfBearish;
}
if (UseAtrFilter)
{
double atrInPips = _atr.Result.Last(1) / Symbol.PipSize;
bool volatilitySufficient = atrInPips >= MinAtrPips;
validLongEnv &= volatilitySufficient;
validShortEnv &= volatilitySufficient;
}
// 3. Execution & Position Management
var activePosition = Positions.Find("M1_Audit", SymbolName);
// Reversion Exit Logic
if (activePosition != null)
{
if (activePosition.TradeType == TradeType.Buy && isShortCross)
ClosePosition(activePosition);
else if (activePosition.TradeType == TradeType.Sell && isLongCross)
ClosePosition(activePosition);
}
// Entry Logic
if (Positions.Find("M1_Audit", SymbolName) == null)
{
if (isLongCross && validLongEnv)
ExecuteMarketOrder(TradeType.Buy, SymbolName, VolumeInUnits, "M1_Audit");
else if (isShortCross && validShortEnv)
ExecuteMarketOrder(TradeType.Sell, SymbolName, VolumeInUnits, "M1_Audit");
}
}
}
}
Re: Why most M1 indicators fail after transaction costs
Posted: Fri Sep 18, 2026 7:03 pm
by PTScalper
Running the Quantitative Audit in cTrader
1.) Open the Automate application in cTrader.
2.) Select the M1CostAudit cBot and add an M1 instance for a major pair (e.g., EURUSD).
3.) In the Backtesting tab, ensure Data is set to Tick data from Server (accurate).
4.) The Friction Test: First, uncheck the Execution Filters and run it. The gross pipeline will look active, but the net equity curve will decay predictably.
5.) Enable the filters. The trade frequency will bottleneck, but the execution quality survives the fixed spread/commission requirements set in your cTrader account properties.
Demonstrating this math via code perfectly illustrates why shifting focus away from lagging M1 indicators toward raw price action and candlestick structure on the 15-minute and Daily timeframes provides a much thicker margin of error. The larger structural moves inherently absorb the frictional costs that completely break high-frequency retail scalping.
Re: Why most M1 indicators fail after transaction costs
Posted: Fri Sep 18, 2026 7:03 pm
by PTScalper
This is the necessary evolution. Shifting entirely away from lagging mathematical averages toward raw price action on the Daily and 15-minute charts is where you actually find structural edge. Indicators tell you what happened; liquidity sweeps tell you what the larger participants are doing right now.
If we strip out the EMAs, we can build a cBot that relies purely on market microstructure. The logic below replaces the moving averages with a 15-minute Liquidity Sweep detector and a Daily Price Action filter.
Here is the structural logic:
The Daily Filter: Instead of a 15m EMA, we look at the Daily chart. If yesterday closed higher than the day before, we are structurally bullish.
The 15m Setup (The Sweep): We define a "Swing Point" by finding the highest high (or lowest low) of the last 20 bars. A sweep occurs when the current 15m bar pierces that structural high to grab buy-side liquidity (triggering retail breakout traders' long entries and early shorters' stop losses), but aggressively rejects and closes back below that high.
The M1 Execution: We run the cBot on the M1 chart. The exact minute the 15m sweep bar closes, the logic executes a market order in the opposite direction of the sweep.
Re: Why most M1 indicators fail after transaction costs
Posted: Fri Sep 18, 2026 7:04 pm
by PTScalper
The Raw Price Action cBot (cAlgo)
Code: Select all
using System;
using cAlgo.API;
using cAlgo.API.Internals;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class LiquiditySweepExecution : Robot
{
[Parameter("Swing Lookback (15m Bars)", Group = "Structure", DefaultValue = 20)]
public int SwingLookback { get; set; }
[Parameter("Align with Daily Trend", Group = "Filters", DefaultValue = true)]
public bool UseDailyBias { get; set; }
[Parameter("Volume (Units)", Group = "Risk", DefaultValue = 100000)]
public double Volume { get; set; }
[Parameter("Stop Loss (Pips)", Group = "Risk", DefaultValue = 10)]
public double StopLoss { get; set; }
[Parameter("Take Profit (Pips)", Group = "Risk", DefaultValue = 20)]
public double TakeProfit { get; set; }
private Bars _m15Bars;
private Bars _dailyBars;
protected override void OnStart()
{
// Load the necessary higher timeframe data series
_m15Bars = MarketData.GetBars(TimeFrame.Minute15);
_dailyBars = MarketData.GetBars(TimeFrame.Daily);
}
protected override void OnBar()
{
// Ensure we only process the logic at the exact close of a 15-minute cycle
if (Bars.OpenTimes.Last(0).Minute % 15 != 0)
return;
// 1. Daily Bias (Raw PA: Are we structurally up or down based on yesterday's close?)
bool dailyBullish = _dailyBars.ClosePrices.Last(1) > _dailyBars.ClosePrices.Last(2);
bool dailyBearish = _dailyBars.ClosePrices.Last(1) < _dailyBars.ClosePrices.Last(2);
// 2. Identify 15m Structural Highs & Lows (Excluding the bar that just closed)
double localHigh = double.MinValue;
double localLow = double.MaxValue;
// Iterate backward to find the strict structural high/low
for (int i = 2; i <= SwingLookback + 1; i++)
{
if (_m15Bars.HighPrices.Last(i) > localHigh)
localHigh = _m15Bars.HighPrices.Last(i);
if (_m15Bars.LowPrices.Last(i) < localLow)
localLow = _m15Bars.LowPrices.Last(i);
}
// 3. Detect the Liquidity Sweep on the recently closed 15m bar
double sweepBarHigh = _m15Bars.HighPrices.Last(1);
double sweepBarLow = _m15Bars.LowPrices.Last(1);
double sweepBarClose = _m15Bars.ClosePrices.Last(1);
// Buy-side liquidity swept: Pierced the high, but closed back below it (Pin bar / Rejection)
bool buySideSwept = (sweepBarHigh > localHigh) && (sweepBarClose < localHigh);
// Sell-side liquidity swept: Pierced the low, but closed back above it
bool sellSideSwept = (sweepBarLow < localLow) && (sweepBarClose > localLow);
// 4. Logic Compilation
bool validLongEntry = sellSideSwept && (!UseDailyBias || dailyBullish);
bool validShortEntry = buySideSwept && (!UseDailyBias || dailyBearish);
// 5. Execution
if (validLongEntry)
{
CloseOppositePositions(TradeType.Sell);
if (Positions.Find("PA_Sweep", SymbolName) == null)
{
ExecuteMarketOrder(TradeType.Buy, SymbolName, Volume, "PA_Sweep", StopLoss, TakeProfit);
}
}
else if (validShortEntry)
{
CloseOppositePositions(TradeType.Buy);
if (Positions.Find("PA_Sweep", SymbolName) == null)
{
ExecuteMarketOrder(TradeType.Sell, SymbolName, Volume, "PA_Sweep", StopLoss, TakeProfit);
}
}
}
private void CloseOppositePositions(TradeType typeToClose)
{
foreach (var position in Positions.FindAll("PA_Sweep", SymbolName))
{
if (position.TradeType == typeToClose)
ClosePosition(position);
}
}
}
}
Re: Why most M1 indicators fail after transaction costs
Posted: Fri Sep 18, 2026 7:04 pm
by PTScalper
Notice how clean the C# is when you aren't managing indicator buffers. Because cTrader isolates the MarketData.GetBars() collections so effectively, pulling structural data from higher timeframes takes a fraction of the compute power compared to MT4.
When you run this on a raw dataset, it completely bypasses the spread-drain issue of the previous EMA model. Instead of entering late after a move has started, you are entering exactly when the smart money is absorbing the retail stop-loss liquidity.
Re: Why most M1 indicators fail after transaction costs
Posted: Fri Sep 18, 2026 7:05 pm
by PTScalper
To implement partial profit taking and a breakeven stop loss in cTrader, we need to introduce an OnTick() override. While entries can wait for the 15-minute bar to close, trade management must monitor real-time tick data to trigger exactly when the 1:1 risk/reward target is hit.
From a C# architecture perspective, the cleanest way to track whether a position has been scaled out is to keep it stateless. Rather than storing position IDs in memory (which would reset if the cBot restarts or you lose connection), we simply check if the current position.VolumeInUnits is less than the initial Volume parameter.
Re: Why most M1 indicators fail after transaction costs
Posted: Fri Sep 18, 2026 7:05 pm
by PTScalper
Here is the updated cBot with the ManageOpenPositions() logic injected into the tick cycle:
Code: Select all
using System;
using cAlgo.API;
using cAlgo.API.Internals;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class LiquiditySweepExecution : Robot
{
[Parameter("Swing Lookback (15m Bars)", Group = "Structure", DefaultValue = 20)]
public int SwingLookback { get; set; }
[Parameter("Align with Daily Trend", Group = "Filters", DefaultValue = true)]
public bool UseDailyBias { get; set; }
[Parameter("Initial Volume (Units)", Group = "Risk", DefaultValue = 100000)]
public double Volume { get; set; }
[Parameter("Stop Loss (Pips)", Group = "Risk", DefaultValue = 10)]
public double StopLoss { get; set; }
[Parameter("Take Profit (Pips)", Group = "Risk", DefaultValue = 20)] // Final Target (e.g., 1:2 RR)
public double TakeProfit { get; set; }
private Bars _m15Bars;
private Bars _dailyBars;
protected override void OnStart()
{
_m15Bars = MarketData.GetBars(TimeFrame.Minute15);
_dailyBars = MarketData.GetBars(TimeFrame.Daily);
}
protected override void OnTick()
{
// Real-time management for scaling out and trailing stops
ManageOpenPositions();
}
protected override void OnBar()
{
// Ensure we only process the entry logic at the exact close of a 15-minute cycle
if (Bars.OpenTimes.Last(0).Minute % 15 != 0)
return;
// 1. Daily Bias
bool dailyBullish = _dailyBars.ClosePrices.Last(1) > _dailyBars.ClosePrices.Last(2);
bool dailyBearish = _dailyBars.ClosePrices.Last(1) < _dailyBars.ClosePrices.Last(2);
// 2. Identify 15m Structural Highs & Lows
double localHigh = double.MinValue;
double localLow = double.MaxValue;
for (int i = 2; i <= SwingLookback + 1; i++)
{
if (_m15Bars.HighPrices.Last(i) > localHigh)
localHigh = _m15Bars.HighPrices.Last(i);
if (_m15Bars.LowPrices.Last(i) < localLow)
localLow = _m15Bars.LowPrices.Last(i);
}
// 3. Detect the Liquidity Sweep
double sweepBarHigh = _m15Bars.HighPrices.Last(1);
double sweepBarLow = _m15Bars.LowPrices.Last(1);
double sweepBarClose = _m15Bars.ClosePrices.Last(1);
bool buySideSwept = (sweepBarHigh > localHigh) && (sweepBarClose < localHigh);
bool sellSideSwept = (sweepBarLow < localLow) && (sweepBarClose > localLow);
// 4. Logic Compilation
bool validLongEntry = sellSideSwept && (!UseDailyBias || dailyBullish);
bool validShortEntry = buySideSwept && (!UseDailyBias || dailyBearish);
// 5. Execution
if (validLongEntry)
{
CloseOppositePositions(TradeType.Sell);
if (Positions.Find("PA_Sweep", SymbolName) == null)
{
ExecuteMarketOrder(TradeType.Buy, SymbolName, Volume, "PA_Sweep", StopLoss, TakeProfit);
}
}
else if (validShortEntry)
{
CloseOppositePositions(TradeType.Buy);
if (Positions.Find("PA_Sweep", SymbolName) == null)
{
ExecuteMarketOrder(TradeType.Sell, SymbolName, Volume, "PA_Sweep", StopLoss, TakeProfit);
}
}
}
private void ManageOpenPositions()
{
foreach (var position in Positions.FindAll("PA_Sweep", SymbolName))
{
// Stateless check: If the volume is lower than the initial parameter, it's already scaled.
if (position.VolumeInUnits < Volume)
continue;
// cAlgo's native .Pips property automatically accounts for the live bid/ask spread
if (position.Pips >= StopLoss)
{
// 1. Calculate 50% volume and snap it to the broker's allowed step size
double halfVolume = Symbol.NormalizeVolumeInUnits(position.VolumeInUnits / 2, RoundingMode.Down);
// 2. Scale out
ClosePosition(position, halfVolume);
// 3. Move stop to Breakeven
ModifyPosition(position, position.EntryPrice, position.TakeProfit);
}
}
}
private void CloseOppositePositions(TradeType typeToClose)
{
foreach (var position in Positions.FindAll("PA_Sweep", SymbolName))
{
if (position.TradeType == typeToClose)
ClosePosition(position);
}
}
}
}