Re: Stop Trading Fixed Lot Sizes: Why Sizing by Pip Distance Saved My Account
Posted: Sat Sep 26, 2026 6:13 pm
1-Click Dynamic Execution cBot (C#)
Code: Select all
using System;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
namespace cAlgo.Robots
{
public enum SlCalculationMode
{
ManualPips,
ATR_Volatility
}
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class DynamicRiskExecutor : Robot
{
[Parameter("Risk Per Trade (%)", DefaultValue = 0.5, MinValue = 0.01, Step = 0.1)]
public double RiskPct { get; set; }
[Parameter("Stop Loss Mode", DefaultValue = SlCalculationMode.ManualPips)]
public SlCalculationMode SlMode { get; set; }
[Parameter("Manual SL (Pips)", DefaultValue = 10.0, MinValue = 0.1)]
public double ManualPips { get; set; }
[Parameter("ATR Period", DefaultValue = 14, MinValue = 1)]
public int AtrPeriod { get; set; }
[Parameter("ATR Multiplier", DefaultValue = 1.5, MinValue = 0.1, Step = 0.1)]
public double AtrMultiplier { get; set; }
private AverageTrueRange _atr;
private TextBlock _uiRisk;
private TextBlock _uiSl;
private TextBlock _uiLots;
// State variables to hold the exact size and SL when a button is clicked
private double _currentVolumeInUnits;
private double _currentSlPips;
protected override void OnStart()
{
_atr = Indicators.AverageTrueRange(AtrPeriod, MovingAverageType.Simple);
BuildUI();
// Run initial calculation so the dashboard populates immediately
UpdateCalculations();
}
protected override void OnTick()
{
// Updates continuously as price and volatility shift
UpdateCalculations();
}
private void UpdateCalculations()
{
double riskDollars = Account.Balance * (RiskPct / 100.0);
_currentSlPips = ManualPips;
if (SlMode == SlCalculationMode.ATR_Volatility)
{
_currentSlPips = (_atr.Result.LastValue / Symbol.PipSize) * AtrMultiplier;
}
if (_currentSlPips <= 0 || Symbol.PipValue == 0) return;
// Calculate raw units and normalize for broker requirements
double rawUnits = riskDollars / (_currentSlPips * Symbol.PipValue);
_currentVolumeInUnits = Symbol.NormalizeVolumeInUnits(rawUnits, RoundingMode.Down);
double lots = Symbol.VolumeInUnitsToQuantity(_currentVolumeInUnits);
// Update UI
_uiRisk.Text = $"Risk: {Account.Currency} {Math.Round(riskDollars, 2)}";
string modeLabel = SlMode == SlCalculationMode.ManualPips ? "Pips" : "Pips (ATR)";
_uiSl.Text = $"SL: {Math.Round(_currentSlPips, 1)} {modeLabel}";
_uiLots.Text = $"Execute: {Math.Round(lots, 2)} Lots";
}
private void BuildUI()
{
var mainPanel = new StackPanel
{
Orientation = Orientation.Vertical,
BackgroundColor = Color.FromArgb(220, 15, 15, 15),
HorizontalAlignment = HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Bottom,
Margin = new Thickness(20),
Width = 180
};
mainPanel.AddChild(new TextBlock
{
Text = "1-CLICK SIZER",
ForegroundColor = Color.White,
BackgroundColor = Color.DodgerBlue,
FontWeight = FontWeight.ExtraBold,
Margin = new Thickness(0, 0, 0, 5),
Padding = new Thickness(5),
TextAlignment = TextAlignment.Center
});
_uiRisk = CreateTextRow(mainPanel, Color.Tomato);
_uiSl = CreateTextRow(mainPanel, Color.LightGray);
_uiLots = CreateTextRow(mainPanel, Color.LimeGreen, true);
// Create horizontal panel for the execution buttons
var buttonPanel = new StackPanel
{
Orientation = Orientation.Horizontal,
Margin = new Thickness(0, 10, 0, 0),
HorizontalAlignment = HorizontalAlignment.Center
};
var buyBtn = new Button
{
Text = "BUY",
BackgroundColor = Color.SeaGreen,
ForegroundColor = Color.White,
Width = 75,
Margin = new Thickness(0, 0, 5, 0)
};
buyBtn.Click += args => ExecuteTrade(TradeType.Buy);
var sellBtn = new Button
{
Text = "SELL",
BackgroundColor = Color.Firebrick,
ForegroundColor = Color.White,
Width = 75
};
sellBtn.Click += args => ExecuteTrade(TradeType.Sell);
buttonPanel.AddChild(buyBtn);
buttonPanel.AddChild(sellBtn);
mainPanel.AddChild(buttonPanel);
Chart.AddControl(mainPanel);
}
private TextBlock CreateTextRow(StackPanel parent, Color color, bool isBold = false)
{
var textBlock = new TextBlock
{
ForegroundColor = color,
FontWeight = isBold ? FontWeight.ExtraBold : FontWeight.Normal,
Margin = new Thickness(5, 2, 5, 2)
};
parent.AddChild(textBlock);
return textBlock;
}
private void ExecuteTrade(TradeType tradeType)
{
if (_currentVolumeInUnits <= 0)
{
Print("Error: Calculated volume is invalid.");
return;
}
// ExecuteMarketOrderAsync prevents the UI from freezing while the broker processes the trade
ExecuteMarketOrderAsync(tradeType, SymbolName, _currentVolumeInUnits, "DynamicSizer", _currentSlPips, null);
}
}
}