Replace your current OnTick() method with this. It loops through the active positions managed by this bot, dynamically calculates the 1:1 price based on the actual executed Stop Loss distance, and triggers the close.
public override void OnTick()
{
// Keeps the UI math updating
UpdateDashboard();
// Iterate only through positions opened by this specific cBot
foreach (var position in Positions.FindAll("SpreadProtected", SymbolName))
{
// Skip if we already took a partial, or if there is no Stop Loss
if (_partiallyClosedPositions.Contains(position.Id) || !position.StopLoss.HasValue)
continue;
// Calculate the absolute risk distance in price
double riskInPrice = Math.Abs(position.EntryPrice - position.StopLoss.Value);
// Calculate the exact 1:1 target price
double target1To1Price = position.TradeType == TradeType.Buy
? position.EntryPrice + riskInPrice
: position.EntryPrice - riskInPrice;
// Check if current price has reached the 1:1 threshold
bool is1To1Reached = position.TradeType == TradeType.Buy
? Symbol.Bid >= target1To1Price
: Symbol.Ask <= target1To1Price;
if (is1To1Reached)
{
// Divide volume by 2 and safely normalize to broker step sizes
double halfVolume = Symbol.NormalizeVolumeInUnits(position.VolumeInUnits / 2, RoundingMode.Down);
if (halfVolume >= Symbol.VolumeInUnitsMin)
{
// 1. Close half the position asynchronously
ClosePositionAsync(position, halfVolume);
// 2. Optional: Roll remaining stop loss to Entry Price (Breakeven)
ModifyPositionAsync(position, position.EntryPrice, position.TakeProfit);
// 3. Mark this position ID so we never process it again
_partiallyClosedPositions.Add(position.Id);
Print("1:1 Reached. Partial taken and SL moved to BE for Position ID: " + position.Id);
}
}
}
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
To keep the cBot running cleanly for weeks at a time, you need to flush the HashSet when a position fully closes (whether by hitting Take Profit, getting stopped out at Breakeven, or being manually closed). Otherwise, the memory footprint will slowly creep up.
protected override void OnPositionsClosed(PositionClosedEventArgs args)
{
// If the fully closed position is in our tracker, remove it
if (_partiallyClosedPositions.Contains(args.Position.Id))
{
_partiallyClosedPositions.Remove(args.Position.Id);
}
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
By using Symbol.NormalizeVolumeInUnits with RoundingMode.Down, you guarantee that if you execute an odd lot size (like 0.03 lots), the bot safely rounds the partial down (closing 0.01 and leaving 0.02) rather than throwing an invalid volume rejection from the broker API.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
To implement a trailing stop that activates only after the 1:1 partial has been taken, we can use the _partiallyClosedPositions state tracker you just built.
The biggest trap when programming trailing stops in the OnTick() method is server spam. Because ticks come in milliseconds apart, if price moves up by a fraction of a pip, your bot might fire 50 overlapping asynchronous ModifyPositionAsync requests before the broker even processes the first one. This will trigger a "Concurrent Modification" error and can get your API connection temporarily blocked.
To solve this, we enforce a Step Buffer. The C# logic will calculate the new trailing stop price, but it will only send the request to the broker if the new stop loss is at least 1 full pip better than the current one.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Replace your OnTick() method with this two-phase structure. If the position ID is not in the tracker, it hunts for the 1:1 target. If it is in the tracker, it switches modes and dynamically trails the Stop Loss behind the current price.
public override void OnTick()
{
// Keeps the UI math updating
UpdateDashboard();
foreach (var position in Positions.FindAll("SpreadProtected", SymbolName))
{
// --- PHASE 1: Wait for 1:1 Partial ---
if (!_partiallyClosedPositions.Contains(position.Id))
{
if (!position.StopLoss.HasValue) continue;
double riskInPrice = Math.Abs(position.EntryPrice - position.StopLoss.Value);
double target1To1Price = position.TradeType == TradeType.Buy
? position.EntryPrice + riskInPrice
: position.EntryPrice - riskInPrice;
bool is1To1Reached = position.TradeType == TradeType.Buy
? Symbol.Bid >= target1To1Price
: Symbol.Ask <= target1To1Price;
if (is1To1Reached)
{
double halfVolume = Symbol.NormalizeVolumeInUnits(position.VolumeInUnits / 2, RoundingMode.Down);
if (halfVolume >= Symbol.VolumeInUnitsMin)
{
ClosePositionAsync(position, halfVolume);
// Move remaining SL to Breakeven
ModifyPositionAsync(position, position.EntryPrice, position.TakeProfit);
// Mark as partialed, activating Phase 2 on the next tick
_partiallyClosedPositions.Add(position.Id);
Print("1:1 Reached. Partial taken. Trailing Stop Activated.");
}
}
}
// --- PHASE 2: Active Trailing Stop ---
else
{
if (!position.StopLoss.HasValue) continue;
double trailDistance = TrailingStopPips * Symbol.PipSize;
// The 1-pip buffer prevents overlapping server modification spam
double stepBuffer = Symbol.PipSize;
if (position.TradeType == TradeType.Buy)
{
// Calculate where the SL should be relative to current Bid
double newSL = Symbol.Bid - trailDistance;
// Only modify if the new SL locks in at least 1 pip more profit than the current SL
if (newSL > position.StopLoss.Value + stepBuffer)
{
double roundedSL = Math.Round(newSL, Symbol.Digits);
ModifyPositionAsync(position, roundedSL, position.TakeProfit);
}
}
else // Sell Position
{
// Calculate where the SL should be relative to current Ask
double newSL = Symbol.Ask + trailDistance;
// Only modify if the new SL locks in at least 1 pip more profit than the current SL
if (newSL < position.StopLoss.Value - stepBuffer)
{
double roundedSL = Math.Round(newSL, Symbol.Digits);
ModifyPositionAsync(position, roundedSL, position.TakeProfit);
}
}
}
}
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Now you have a fully-fledged execution terminal. You identify the structural liquidity sweep, map the Red/Green lines on your chart, and hit BUY.
The cBot verifies the spread won't kill your math, calculates exactly how many lots equal 1% of your equity, fires the market order, monitors it until it hits 1:1, rips off half the volume, moves the stop to breakeven, and immediately switches into an algorithmic trailing mode that rides the runner pip-by-pip until the trend reverses.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.