Page 1 of 2

Aggregating Liquidity: How to Scale Into Sweeps Using Cost Averaging

Posted: Mon Oct 05, 2026 8:03 am
by PTScalper
Hi traders, scalpers,

i decided to write new thread about Liquidity and how to aggregate it, if you will need to open bigger positions (200lots+) with better average spread.
Because it is not true, that if you have bigger risk capital, its easier to trade. I got it, yes, in some point of view like risk etc. it seems to be easier, but on another hand
you can't jump in and out of the market so quickly like in case smaller account with smaller positions.

When we spot a deep liquidity sweep on the 15-minute or 1-minute chart, trying to snipe the exact reversal point with a single, massive position is risky. More often than not, the price will push just a bit further to hunt the last remaining stops, dragging your entry into unnecessary drawdown.

Instead of trying to catch the absolute bottom or top, I prefer to aggregate liquidity dynamically. By using a cost-averaging (scale-in) strategy, we can slice our intended full size into tiers. I’ve written a lightweight C# cBot for cTrader called Liquidity Aggregator to automate this execution.

Re: Aggregating Liquidity: How to Scale Into Sweeps Using Cost Averaging

Posted: Mon Oct 05, 2026 8:04 am
by PTScalper
How It Works
This is designed as a "fire-and-forget" discretionary execution tool. You don't leave this running 24/7. When price enters your area of interest (POI), you simply attach the bot to the chart and let it handle the microstructure execution.

Initial Entry: It immediately opens your baseline position.

Scaling In: If the market pushes further into the sweep zone by a predefined distance (e.g., 10 pips), it automatically opens another tier. The size of this new tier is increased by a Volume Multiplier to pull the average entry price closer to current market action.

Dynamic Take Profit: Every time a new tier is filled, the bot recalculates the global break-even price of the entire basket and adjusts the Take Profit for all positions so you exit the entire sequence at a net profit.

Auto-Stop: Once the basket hits the collective Take Profit, the bot closes the trades and automatically stops itself to prevent accidental re-entries.

Settings Breakdown

Trade Direction: Set to Buy or Sell depending on the sweep.

Initial Volume: Your starting size (in units).

Volume Multiplier: How much larger each subsequent scale-in order should be. (1.5 is a solid mathematical balance; 2.0 turns it into an aggressive martingale).

Scale-In Distance (Pips): The adverse excursion required before the next tier triggers.

Target Net Profit (Pips): The distance from your average break-even price where the entire basket will close.

Max Positions: A hard cap to protect your equity. If set to 5, it will never open a 6th trade, even if the price keeps dropping.

You can copy the source code above and compile it directly in your cTrader Automate tab. Let me know if you run into any issues compiling or if you have ideas for additional features!

Re: Aggregating Liquidity: How to Scale Into Sweeps Using Cost Averaging

Posted: Mon Oct 05, 2026 8:05 am
by PTScalper
Ctrader V1.0

Code: Select all

using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class LiquidityAggregator : Robot
    {
        [Parameter("Trade Direction", DefaultValue = TradeType.Buy)]
        public TradeType Direction { get; set; }

        [Parameter("Initial Volume (Units)", DefaultValue = 10000, MinValue = 1000, Step = 1000)]
        public double InitialVolume { get; set; }

        [Parameter("Volume Multiplier", DefaultValue = 1.5, MinValue = 1.0, Step = 0.1)]
        public double VolumeMultiplier { get; set; }

        [Parameter("Scale-In Distance (Pips)", DefaultValue = 10.0)]
        public double DistancePips { get; set; }

        [Parameter("Target Net Profit (Pips)", DefaultValue = 5.0)]
        public double TakeProfitPips { get; set; }

        [Parameter("Max Positions", DefaultValue = 5, MinValue = 1)]
        public int MaxPositions { get; set; }

        private string _label = "LiquidityAggregator";
        private bool _isGridActive = false;

        protected override void OnStart()
        {
            // Execute the initial baseline position as soon as the cBot is attached
            var result = ExecuteMarketOrder(Direction, SymbolName, InitialVolume, _label);
            
            if (result.IsSuccessful)
            {
                _isGridActive = true;
                UpdateTakeProfit();
            }
            else
            {
                Print("Failed to execute initial order. Stopping cBot.");
                Stop();
            }
        }

        protected override void OnTick()
        {
            if (!_isGridActive) return;

            var positions = Positions.FindAll(_label, SymbolName, Direction);

            // If the grid was active but now there are no positions, the TP target was hit.
            if (positions.Length == 0)
            {
                Print("Target reached and basket closed. Stopping cBot.");
                Stop();
                return;
            }

            // Halt scaling in if the hard cap is reached
            if (positions.Length >= MaxPositions)
                return;

            // Find the most disadvantageous entry price in the current basket
            double lastPrice = Direction == TradeType.Buy 
                ? positions.Min(p => p.EntryPrice) 
                : positions.Max(p => p.EntryPrice);

            bool shouldScaleIn = false;
            
            if (Direction == TradeType.Buy && Symbol.Ask <= lastPrice - (DistancePips * Symbol.PipSize))
            {
                shouldScaleIn = true;
            }
            else if (Direction == TradeType.Sell && Symbol.Bid >= lastPrice + (DistancePips * Symbol.PipSize))
            {
                shouldScaleIn = true;
            }

            if (shouldScaleIn)
            {
                // Calculate the new volume using the multiplier against the last opened tier
                var lastPosition = positions.OrderBy(p => p.EntryTime).Last();
                double newVolume = Symbol.NormalizeVolumeInUnits(lastPosition.VolumeInUnits * VolumeMultiplier);
                
                var scaleInResult = ExecuteMarketOrder(Direction, SymbolName, newVolume, _label);
                
                if (scaleInResult.IsSuccessful)
                {
                    UpdateTakeProfit();
                }
            }
        }

        private void UpdateTakeProfit()
        {
            var positions = Positions.FindAll(_label, SymbolName, Direction);
            if (positions.Length == 0) return;

            double totalVolume = 0;
            double weightedPriceSum = 0;

            // Aggregate total volume and weighted prices
            foreach (var pos in positions)
            {
                totalVolume += pos.VolumeInUnits;
                weightedPriceSum += pos.EntryPrice * pos.VolumeInUnits;
            }

            // Calculate the global break-even price for the basket
            double averagePrice = weightedPriceSum / totalVolume;
            
            // Offset the break-even price by the desired net profit target
            double targetPrice = Direction == TradeType.Buy 
                ? averagePrice + (TakeProfitPips * Symbol.PipSize)
                : averagePrice - (TakeProfitPips * Symbol.PipSize);

            targetPrice = Math.Round(targetPrice, Symbol.Digits);

            // Apply the new dynamic Take Profit to all open positions in the basket
            foreach (var pos in positions)
            {
                ModifyPosition(pos, pos.StopLoss, targetPrice);
            }
        }
    }
}

Re: Aggregating Liquidity: How to Scale Into Sweeps Using Cost Averaging

Posted: Mon Oct 05, 2026 8:05 am
by PTScalper
Both versions execute the exact same logic as your cBot. They open the baseline order immediately upon attaching to the chart, scale in dynamically against adverse excursions, continuously adjust a unified Take Profit for the basket, and automatically remove themselves (ExpertRemove()) from the chart when the positions clear.

Because they require a price feed to monitor adverse excursions and recalculate the grid, both must be compiled as Expert Advisors (saved in the MQL4\Experts and MQL5\Experts folders), not as Scripts.

Re: Aggregating Liquidity: How to Scale Into Sweeps Using Cost Averaging

Posted: Mon Oct 05, 2026 8:05 am
by PTScalper
MetaTrader 4 (MQL4) Implementation

Code: Select all

//+------------------------------------------------------------------+
//|                                          LiquidityAggregator.mq4 |
//+------------------------------------------------------------------+
#property strict
#property description "Discretionary fire-and-forget liquidity scaling EA"

enum ENUM_TRADE_DIR {
    DIR_BUY = 0,  // Buy
    DIR_SELL = 1  // Sell
};

input ENUM_TRADE_DIR TradeDirection = DIR_BUY;
input double InitialVolume = 0.1;
input double VolumeMultiplier = 1.5;
input double DistancePips = 10.0;
input double TakeProfitPips = 5.0;
input int MaxPositions = 5;
input int MagicNumber = 123456;

bool isGridActive = false;
double pipSize;

int GetVolumeDecimals(double step)
{
    if (step <= 0.001) return 3;
    if (step <= 0.01) return 2;
    if (step <= 0.1) return 1;
    return 0;
}

int OnInit()
{
    pipSize = Point;
    if (Digits == 3 || Digits == 5) pipSize = Point * 10;
    
    double price = (TradeDirection == DIR_BUY) ? Ask : Bid;
    int cmd = (TradeDirection == DIR_BUY) ? OP_BUY : OP_SELL;
    
    int ticket = OrderSend(Symbol(), cmd, InitialVolume, price, 3, 0, 0, "LiquidityAggregator", MagicNumber, 0, clrBlue);
    if (ticket > 0)
    {
        isGridActive = true;
        UpdateTakeProfit();
    }
    else
    {
        Print("Failed to execute initial order. Error: ", GetLastError());
        ExpertRemove();
    }
    return(INIT_SUCCEEDED);
}

void OnTick()
{
    if (!isGridActive) return;
    
    int posCount = 0;
    double lastPrice = (TradeDirection == DIR_BUY) ? 9999999.0 : 0.0;
    double lastLot = 0.0;
    datetime lastTime = 0;
    
    for (int i = OrdersTotal() - 1; i >= 0; i--)
    {
        if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            if (OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
            {
                posCount++;
                if (OrderOpenTime() > lastTime)
                {
                    lastTime = OrderOpenTime();
                    lastLot = OrderLots();
                }
                
                if (TradeDirection == DIR_BUY && OrderOpenPrice() < lastPrice)
                    lastPrice = OrderOpenPrice();
                else if (TradeDirection == DIR_SELL && OrderOpenPrice() > lastPrice)
                    lastPrice = OrderOpenPrice();
            }
        }
    }
    
    if (posCount == 0)
    {
        Print("Target reached and basket closed. Stopping EA.");
        ExpertRemove();
        return;
    }
    
    if (posCount >= MaxPositions) return;
    
    bool shouldScaleIn = false;
    if (TradeDirection == DIR_BUY && Ask <= lastPrice - (DistancePips * pipSize))
        shouldScaleIn = true;
    else if (TradeDirection == DIR_SELL && Bid >= lastPrice + (DistancePips * pipSize))
        shouldScaleIn = true;
        
    if (shouldScaleIn)
    {
        double step = MarketInfo(Symbol(), MODE_LOTSTEP);
        double minLot = MarketInfo(Symbol(), MODE_MINLOT);
        double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
        
        double newVol = MathRound((lastLot * VolumeMultiplier) / step) * step;
        newVol = NormalizeDouble(newVol, GetVolumeDecimals(step));
        newVol = MathMax(minLot, MathMin(maxLot, newVol));
        
        double price = (TradeDirection == DIR_BUY) ? Ask : Bid;
        int cmd = (TradeDirection == DIR_BUY) ? OP_BUY : OP_SELL;
        
        int ticket = OrderSend(Symbol(), cmd, newVol, price, 3, 0, 0, "LiquidityAggregator", MagicNumber, 0, clrRed);
        if (ticket > 0)
        {
            UpdateTakeProfit();
        }
    }
}

void UpdateTakeProfit()
{
    double totalVol = 0;
    double weightedPrice = 0;
    
    for (int i = OrdersTotal() - 1; i >= 0; i--)
    {
        if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            if (OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
            {
                totalVol += OrderLots();
                weightedPrice += OrderOpenPrice() * OrderLots();
            }
        }
    }
    
    if (totalVol == 0) return;
    
    double avgPrice = weightedPrice / totalVol;
    double targetPrice = (TradeDirection == DIR_BUY) 
        ? avgPrice + (TakeProfitPips * pipSize) 
        : avgPrice - (TakeProfitPips * pipSize);
        
    targetPrice = NormalizeDouble(targetPrice, Digits);
    
    for (int i = OrdersTotal() - 1; i >= 0; i--)
    {
        if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            if (OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
            {
                if (NormalizeDouble(OrderTakeProfit(), Digits) != targetPrice)
                {
                    OrderModify(OrderTicket(), OrderOpenPrice(), OrderStopLoss(), targetPrice, 0, clrGreen);
                }
            }
        }
    }
}

Re: Aggregating Liquidity: How to Scale Into Sweeps Using Cost Averaging

Posted: Mon Oct 05, 2026 8:06 am
by PTScalper
MetaTrader 5 (MQL5) Implementation

Note: MT5 structure requires a Hedging account to open simultaneous tiers in the same direction, which the OnInit function verifies before execution.

Code: Select all

//+------------------------------------------------------------------+
//|                                          LiquidityAggregator.mq5 |
//+------------------------------------------------------------------+
#property strict
#property description "Discretionary fire-and-forget liquidity scaling EA"

#include <Trade\Trade.mqh>

enum ENUM_TRADE_DIR {
    DIR_BUY = 0,  // Buy
    DIR_SELL = 1  // Sell
};

input ENUM_TRADE_DIR TradeDirection = DIR_BUY;
input double InitialVolume = 0.1;
input double VolumeMultiplier = 1.5;
input double DistancePips = 10.0;
input double TakeProfitPips = 5.0;
input int MaxPositions = 5;
input ulong MagicNumber = 123456;

CTrade trade;
bool isGridActive = false;
double pipSize;

int GetVolumeDecimals(double step)
{
    if (step <= 0.001) return 3;
    if (step <= 0.01) return 2;
    if (step <= 0.1) return 1;
    return 0;
}

int OnInit()
{
    if ((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE) == ACCOUNT_MARGIN_MODE_RETAIL_NETTING)
    {
        Print("Liquidity Aggregator requires a Hedging account to track separate tiers.");
        return(INIT_FAILED);
    }

    trade.SetExpertMagicNumber(MagicNumber);
    trade.SetDeviationInPoints(30);
    
    pipSize = _Point;
    if (_Digits == 3 || _Digits == 5) pipSize = _Point * 10;
    
    double price = (TradeDirection == DIR_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
    
    bool res = (TradeDirection == DIR_BUY) 
        ? trade.Buy(InitialVolume, _Symbol, price) 
        : trade.Sell(InitialVolume, _Symbol, price);
        
    if (res)
    {
        isGridActive = true;
        UpdateTakeProfit();
    }
    else
    {
        Print("Failed to execute initial order. Result code: ", trade.ResultRetcode());
        ExpertRemove();
    }
    
    return(INIT_SUCCEEDED);
}

void OnTick()
{
    if (!isGridActive) return;
    
    int posCount = 0;
    double lastPrice = (TradeDirection == DIR_BUY) ? 9999999.0 : 0.0;
    double lastLot = 0.0;
    datetime lastTime = 0;
    
    for (int i = PositionsTotal() - 1; i >= 0; i--)
    {
        ulong ticket = PositionGetTicket(i);
        if (ticket > 0)
        {
            if (PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber)
            {
                posCount++;
                datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
                if (openTime > lastTime)
                {
                    lastTime = openTime;
                    lastLot = PositionGetDouble(POSITION_VOLUME);
                }
                
                double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
                if (TradeDirection == DIR_BUY && openPrice < lastPrice)
                    lastPrice = openPrice;
                else if (TradeDirection == DIR_SELL && openPrice > lastPrice)
                    lastPrice = openPrice;
            }
        }
    }
    
    if (posCount == 0)
    {
        Print("Target reached and basket closed. Stopping EA.");
        ExpertRemove();
        return;
    }
    
    if (posCount >= MaxPositions) return;
    
    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    
    bool shouldScaleIn = false;
    if (TradeDirection == DIR_BUY && ask <= lastPrice - (DistancePips * pipSize))
        shouldScaleIn = true;
    else if (TradeDirection == DIR_SELL && bid >= lastPrice + (DistancePips * pipSize))
        shouldScaleIn = true;
        
    if (shouldScaleIn)
    {
        double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
        double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
        double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
        
        double newVol = MathRound((lastLot * VolumeMultiplier) / step) * step;
        newVol = NormalizeDouble(newVol, GetVolumeDecimals(step));
        newVol = MathMax(minLot, MathMin(maxLot, newVol));
        
        bool res = (TradeDirection == DIR_BUY) 
            ? trade.Buy(newVol, _Symbol, ask) 
            : trade.Sell(newVol, _Symbol, bid);
            
        if (res)
        {
            UpdateTakeProfit();
        }
    }
}

void UpdateTakeProfit()
{
    double totalVol = 0;
    double weightedPrice = 0;
    
    for (int i = PositionsTotal() - 1; i >= 0; i--)
    {
        ulong ticket = PositionGetTicket(i);
        if (ticket > 0)
        {
            if (PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber)
            {
                double vol = PositionGetDouble(POSITION_VOLUME);
                totalVol += vol;
                weightedPrice += PositionGetDouble(POSITION_PRICE_OPEN) * vol;
            }
        }
    }
    
    if (totalVol == 0) return;
    
    double avgPrice = weightedPrice / totalVol;
    double targetPrice = (TradeDirection == DIR_BUY)
        ? avgPrice + (TakeProfitPips * pipSize)
        : avgPrice - (TakeProfitPips * pipSize);
        
    targetPrice = NormalizeDouble(targetPrice, _Digits);
    
    for (int i = PositionsTotal() - 1; i >= 0; i--)
    {
        ulong ticket = PositionGetTicket(i);
        if (ticket > 0)
        {
            if (PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber)
            {
                double currentTP = PositionGetDouble(POSITION_TP);
                if (NormalizeDouble(currentTP, _Digits) != targetPrice)
                {
                    trade.PositionModify(ticket, PositionGetDouble(POSITION_SL), targetPrice);
                }
            }
        }
    }
}

Re: Aggregating Liquidity: How to Scale Into Sweeps Using Cost Averaging

Posted: Mon Oct 05, 2026 8:07 am
by PTScalper
Because Pine Script is a charting and backtesting language rather than a direct trade execution environment, it operates on a historical event loop and real-time ticks rather than a "drag-and-drop" manual attachment.

To replicate the "fire-and-forget" discretionary execution of the cTrader/MetaTrader versions in TradingView, this script uses a Start Time trigger. You set the time you want the basket to begin, and the script executes the first tier immediately on that candle. It will track the adverse excursion, scale in, dynamically update the exit limit order, and—crucially—lock itself out from re-entering once the target is hit.

Re: Aggregating Liquidity: How to Scale Into Sweeps Using Cost Averaging

Posted: Mon Oct 05, 2026 8:07 am
by PTScalper
Here is the Pine Script v5 implementation.

Code: Select all

//@version=5
strategy("Liquidity Aggregator [Discretionary]", overlay=true, pyramiding=20, calc_on_every_tick=true)

// =========================================================================
// INPUTS
// =========================================================================
var grp_main = "Grid Settings"
i_direction      = input.string("Long", "Trade Direction", options=["Long", "Short"], group=grp_main)
i_initial_qty    = input.float(1.0, "Initial Volume (Qty)", step=0.01, group=grp_main)
i_multiplier     = input.float(1.5, "Volume Multiplier", step=0.1, group=grp_main)
i_distance_pips  = input.float(10.0, "Scale-In Distance (Pips)", step=1.0, group=grp_main)
i_tp_pips        = input.float(5.0, "Target Net Profit (Pips)", step=1.0, group=grp_main)
i_max_tiers      = input.int(5, "Max Positions", group=grp_main)

var grp_time = "Execution Trigger"
i_start_time     = input.time(timestamp("2026-10-05T00:00:00"), "Start Time (Set to NOW to execute)", group=grp_time)

// =========================================================================
// VARIABLES & PIP CALCULATION
// =========================================================================
var bool  is_active      = false
var bool  trade_finished = false
var int   current_tier   = 0
var float last_price     = na
var float next_qty       = i_initial_qty

// Normalize pip size (Multiply by 10 for standard Forex pairs, otherwise use mintick)
float pip_size = syminfo.mintick * (syminfo.type == "forex" ? 10 : 1)
float distance_raw = i_distance_pips * pip_size
float tp_raw = i_tp_pips * pip_size

bool is_long = i_direction == "Long"

// =========================================================================
// CORE LOGIC
// =========================================================================

// 1. Check if the strategy hit TP and closed the basket
if is_active and strategy.position_size == 0 and current_tier > 0
    is_active := false
    trade_finished := true // Locks the script to prevent accidental re-entries

// 2. Initial Entry Trigger
if time >= i_start_time and not is_active and not trade_finished
    id = "Tier_1"
    if is_long
        strategy.entry(id, strategy.long, qty=i_initial_qty)
    else
        strategy.entry(id, strategy.short, qty=i_initial_qty)
        
    is_active := true
    current_tier := 1
    last_price := close
    next_qty := i_initial_qty * i_multiplier

// 3. Scale-In Logic
if is_active and current_tier < i_max_tiers
    bool should_scale = false
    
    if is_long and close <= (last_price - distance_raw)
        should_scale := true
    else if not is_long and close >= (last_price + distance_raw)
        should_scale := true
        
    if should_scale
        current_tier += 1
        id = "Tier_" + str.tostring(current_tier)
        
        if is_long
            strategy.entry(id, strategy.long, qty=next_qty)
        else
            strategy.entry(id, strategy.short, qty=next_qty)
            
        last_price := close // Update to the new lowest/highest tier price
        next_qty := next_qty * i_multiplier // Exponentially increase next tier

// 4. Dynamic Take Profit
float target_price = na
if strategy.position_size != 0
    float avg_price = strategy.position_avg_price
    target_price := is_long ? avg_price + tp_raw : avg_price - tp_raw
    
    // Submit the collective limit order for the entire basket
    strategy.exit("Exit_Basket", limit=target_price)

// =========================================================================
// VISUALIZATION
// =========================================================================
// Plots the unified Break-Even price and the dynamic Take Profit target on the chart
plot(strategy.position_size != 0 ? strategy.position_avg_price : na, "BE Price", color.new(color.yellow, 0), style=plot.style_linebr, linewidth=1)
plot(strategy.position_size != 0 ? target_price : na, "Take Profit", color.new(color.green, 0), style=plot.style_linebr, linewidth=2)

Re: Aggregating Liquidity: How to Scale Into Sweeps Using Cost Averaging

Posted: Mon Oct 05, 2026 8:07 am
by PTScalper
How to use this in TradingView:

Execution via Webhooks: Because TradingView cannot place trades directly on your broker, you will need to tie the strategy.entry and strategy.exit alerts to an execution bridge like PineConnector or 3Commas.

The "Start Time" Trigger: When you see a sweep forming on your 1m/15m chart and want to execute, open the Strategy Settings, change the Start Time to the current candlestick time, and click OK. The script will immediately enter the market and manage the grid dynamically on every incoming tick (calc_on_every_tick=true).

Resetting for the Next Trade: After the basket hits the Take Profit, the script locks itself (trade_finished = true). To take a new setup later in the session, you must advance the Start Time input to the new current time—this resets the state variables and arms the script again.

Visuals: I added plotting at the bottom of the script. When the grid is active, it draws a yellow line at your exact volume-weighted average price and a thick green line at your dynamic take profit, updating in real-time as new tiers execute.

Re: Aggregating Liquidity: How to Scale Into Sweeps Using Cost Averaging

Posted: Mon Oct 05, 2026 8:08 am
by PTScalper
To elevate this cBot from a basic script to a "professional" execution tool, we need to add several layers of institutional-grade logic and quality-of-life features.

Here is the Pro Version.

What makes this "Pro":

On-Chart HUD (Heads-Up Display): A real-time UI panel right on your chart showing active tiers, total volume, exact break-even price, current floating PnL, and distance to the target.

Spread Filter: Liquidity sweeps often feature massive spread widening (especially at rollover or during news). The bot now pauses scaling in if the spread exceeds a maximum limit, preventing you from getting filled at terrible prices.

Asynchronous Execution: It uses ExecuteMarketOrderAsync and ModifyPositionAsync. This prevents the bot from freezing the cTrader platform during high-volatility tick spikes.

Event-Driven Closure: Instead of polling on every tick to see if trades are closed, it listens to the Positions.Closed event, making it incredibly lightweight on CPU resources.

Slippage Control: Added a parameter to define max acceptable slippage for entry orders.