For Ctrader and ICtrader traders i have prepared this version
MetaTrader forces you to build complex workarounds for basic market mechanics—like "ticket splitting" on partial closes and manual point-to-pip normalizations for 3-digit silver brokers. The cAlgo API handles all of this natively. In cTrader:
Partial Closes don't destroy the Position ID. position.Close(volume) simply reduces the size of the open position. Your tracking logic remains perfectly intact.
Symbol.PipSize is absolute. cTrader abstracts away the fractional tick sizing in the backend, meaning your pip calculations work exactly the same on a 2-digit, 3-digit, or fractional broker.
Here is the complete, professional-grade C# cBot architecture for the Silver Bullet manager.
The cTrader C# Implementation
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 SilverBulletManager : Robot
{
[Parameter("Risk Percent", DefaultValue = 1.0, Group = "Risk & Sizing", MinValue = 0.1)]
public double RiskPercent { get; set; }
[Parameter("Max Spread (Pips)", DefaultValue = 4.0, Group = "Risk & Sizing")]
public double MaxSpreadPips { get; set; }
[Parameter("RR Target", DefaultValue = 1.0, Group = "Trade Management")]
public double RR_Target { get; set; }
[Parameter("Partial Close %", DefaultValue = 50.0, Group = "Trade Management", MinValue = 10, MaxValue = 90)]
public double PartialClosePct { get; set; }
[Parameter("Breakeven Offset (Pips)", DefaultValue = 1.0, Group = "Trade Management")]
public double BreakevenOffsetPips { get; set; }
[Parameter("Trailing Distance (Pips)", DefaultValue = 15.0, Group = "Trade Management")]
public double TrailingDistPips { get; set; }
[Parameter("Trailing Step (Pips)", DefaultValue = 2.0, Group = "Trade Management")]
public double TrailingStepPips { get; set; }
// Unique identifier for the strategy's trades
private const string Label = "SilverBullet";
protected override void OnTick()
{
// 1. Defend open capital first
ManageOpenPositions();
ApplyTrailingStop();
// 2. Scan & execute new entries here
// if (IsSilverBulletWindow() && IsExecutionSafe()) {
// PlaceLimitOrder(TradeType.Buy, SymbolName, volume, entry, Label, slPips, tpPips);
// }
}
//+------------------------------------------------------------------+
//| Dynamic Sizing & Safety |
//+------------------------------------------------------------------+
public bool IsExecutionSafe()
{
// cTrader directly provides spread divided by PipSize
double currentSpread = Symbol.Spread / Symbol.PipSize;
if (currentSpread > MaxSpreadPips)
{
Print("Execution Blocked: Spread {0} pips > {1} pips", Math.Round(currentSpread, 1), MaxSpreadPips);
return false;
}
return true;
}
public double CalculateVolume(double entryPrice, double stopLossPrice)
{
double riskAmount = Account.Balance * (RiskPercent / 100.0);
double slDistancePips = Math.Abs(entryPrice - stopLossPrice) / Symbol.PipSize;
if (slDistancePips == 0) return 0;
// Symbol.PipValue in cTrader is the value of 1 pip for Symbol.VolumeInUnitsMin
double riskPerMinVolume = slDistancePips * Symbol.PipValue;
double rawVolume = (riskAmount / riskPerMinVolume) * Symbol.VolumeInUnitsMin;
// Natively normalizes to the broker's minimum, maximum, and lot step limits
return Symbol.NormalizeVolumeInUnits(rawVolume, RoundingMode.Down);
}
//+------------------------------------------------------------------+
//| Trade Management: Native Partial Close |
//+------------------------------------------------------------------+
private void ManageOpenPositions()
{
// Easily isolate positions managed by this specific cBot
var positions = Positions.FindAll(Label, SymbolName);
foreach (var position in positions)
{
if (!position.StopLoss.HasValue) continue;
double bePrice = position.TradeType == TradeType.Buy
? position.EntryPrice + (BreakevenOffsetPips * Symbol.PipSize)
: position.EntryPrice - (BreakevenOffsetPips * Symbol.PipSize);
// State check: Skip if SL is already moved to/past BE
if ((position.TradeType == TradeType.Buy && position.StopLoss.Value >= bePrice) ||
(position.TradeType == TradeType.Sell && position.StopLoss.Value <= bePrice))
{
continue;
}
double riskDist = Math.Abs(position.EntryPrice - position.StopLoss.Value);
if (riskDist == 0) continue;
bool targetHit = false;
if (position.TradeType == TradeType.Buy && Symbol.Bid >= position.EntryPrice + (riskDist * RR_Target)) targetHit = true;
if (position.TradeType == TradeType.Sell && Symbol.Ask <= position.EntryPrice - (riskDist * RR_Target)) targetHit = true;
if (targetHit)
{
// 1. Move SL to Breakeven
var modifyResult = position.ModifyStopLossPrice(bePrice);
// 2. Execute Partial Volume Close
if (modifyResult.IsSuccessful)
{
double volumeToClose = Symbol.NormalizeVolumeInUnits(position.VolumeInUnits * (PartialClosePct / 100.0), RoundingMode.Down);
if (volumeToClose >= Symbol.VolumeInUnitsMin && volumeToClose < position.VolumeInUnits)
{
// cTrader simply reduces the position volume without creating a new ticket
position.Close(volumeToClose);
}
}
}
}
}
//+------------------------------------------------------------------+
//| Trailing Stop Engine |
//+------------------------------------------------------------------+
private void ApplyTrailingStop()
{
var positions = Positions.FindAll(Label, SymbolName);
foreach (var position in positions)
{
if (!position.StopLoss.HasValue) continue;
double bePrice = position.TradeType == TradeType.Buy
? position.EntryPrice + (BreakevenOffsetPips * Symbol.PipSize)
: position.EntryPrice - (BreakevenOffsetPips * Symbol.PipSize);
// Ensure position is past BE before trailing
bool isPastBE = false;
if (position.TradeType == TradeType.Buy && position.StopLoss.Value >= bePrice) isPastBE = true;
if (position.TradeType == TradeType.Sell && position.StopLoss.Value <= bePrice) isPastBE = true;
if (!isPastBE) continue;
double trailPoints = TrailingDistPips * Symbol.PipSize;
double stepPoints = TrailingStepPips * Symbol.PipSize;
if (position.TradeType == TradeType.Buy)
{
double newSL = Symbol.Bid - trailPoints;
// Step filter to prevent server spam
if (newSL > position.StopLoss.Value + stepPoints)
{
position.ModifyStopLossPrice(newSL);
}
}
else if (position.TradeType == TradeType.Sell)
{
double newSL = Symbol.Ask + trailPoints;
// Step filter to prevent server spam
if (newSL < position.StopLoss.Value - stepPoints)
{
position.ModifyStopLossPrice(newSL);
}
}
}
}
}
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.