Page 2 of 2

Re: My perspective on scalping – Forex for scaling capital, Stocks for steady cash flow

Posted: Sun Sep 06, 2026 2:02 pm
by PTScalper
Here is the complete implementation for cTrader (C#).

When porting this engine to cAlgo, the C# environment provides a massive advantage over MQL4/MQL5 because cTrader natively handles exact pip values, dynamic tick scaling, and precise volume normalization right out of the box. This makes the math extremely reliable whether you are running high-volume scalping on standard Forex pairs or volatile metals like Silver.

💻 cTrader (C#): Pro MM Asymmetric cBot

Code: Select all

using System;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    public enum MmSizingModel
    {
        Linear,
        Exponential,
        ProfitCompound
    }

    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class ProMMAsymmetricScalping : Robot
    {
        // =========================================================================
        // 1. ADVANCED INPUTS & CONFIGURATION
        // =========================================================================
        [Parameter("Sizing Model", DefaultValue = MmSizingModel.ProfitCompound, Group = "Money Management")]
        public MmSizingModel MmType { get; set; }

        [Parameter("Base Risk %", DefaultValue = 1.0, MinValue = 0.1, Step = 0.1, Group = "Money Management")]
        public double BaseRiskPct { get; set; }

        [Parameter("Profit Compound Multiplier", DefaultValue = 2.0, MinValue = 0.1, Step = 0.1, Group = "Money Management")]
        public double ProfitMultiplier { get; set; }

        [Parameter("Hard Equity Risk Cap %", DefaultValue = 5.0, MinValue = 0.1, Step = 0.1, Group = "Money Management")]
        public double HardCapPct { get; set; }

        [Parameter("Manual Init Deposit (0 = Auto)", DefaultValue = 0.0, Group = "Money Management")]
        public double ManualInitDeposit { get; set; }

        [Parameter("ATR Period", DefaultValue = 14, Group = "Volatility & Exits")]
        public int AtrPeriod { get; set; }

        [Parameter("SL (ATR Multiplier)", DefaultValue = 1.5, Group = "Volatility & Exits")]
        public double AtrMulSL { get; set; }

        [Parameter("Risk:Reward Ratio", DefaultValue = 2.0, Group = "Volatility & Exits")]
        public double RrRatio { get; set; }

        // =========================================================================
        // GLOBAL VARIABLES
        // =========================================================================
        private double _initialCapital;
        private AverageTrueRange _atr;

        protected override void OnStart()
        {
            // Lock in the initial capital for the asymmetric profit calculation
            _initialCapital = ManualInitDeposit > 0 ? ManualInitDeposit : Account.Balance;
            
            // Initialize the ATR indicator
            _atr = Indicators.AverageTrueRange(MarketSeries, AtrPeriod, MovingAverageType.Simple);
        }

        // =========================================================================
        // LOT NORMALIZATION & SIZING ENGINE
        // =========================================================================
        private double CalculateVolumeInUnits(double slDistancePips)
        {
            if (slDistancePips <= 0) return 0;

            double equity = Account.Equity;
            double profits = Math.Max(0, equity - _initialCapital);
            double targetRisk = 0;

            // 1. Calculate target risk capital ($)
            switch (MmType)
            {
                case MmSizingModel.Linear:
                    targetRisk = _initialCapital * (BaseRiskPct / 100.0);
                    break;
                case MmSizingModel.Exponential:
                    targetRisk = equity * (BaseRiskPct / 100.0);
                    break;
                case MmSizingModel.ProfitCompound:
                    double baseRisk = _initialCapital * (BaseRiskPct / 100.0);
                    double profitRisk = profits * ((BaseRiskPct * ProfitMultiplier) / 100.0);
                    targetRisk = baseRisk + profitRisk;
                    break;
            }

            // 2. Apply Hard Equity Risk Cap
            double maxRiskAllowed = equity * (HardCapPct / 100.0);
            targetRisk = Math.Min(targetRisk, maxRiskAllowed);

            // 3. Calculate volume based on monetary risk
            // cTrader natively tracks PipValue per unit, making this step flawless across all assets
            double lossPerUnit = slDistancePips * Symbol.PipValue;

            if (lossPerUnit <= 0) return 0;
            double rawVolume = targetRisk / lossPerUnit;

            // 4. Truncate to exact broker specifications
            return Symbol.NormalizeVolumeInUnits(rawVolume, RoundingMode.Down);
        }

        // =========================================================================
        // EXECUTION WRAPPER
        // =========================================================================
        public void ExecuteTrade(TradeType tradeType)
        {
            // Ensure ATR is fully loaded before calculating distances
            if (double.IsNaN(_atr.Result.LastValue)) return;

            // ATR is in exact price points, convert it to standard Pips
            double slDistancePoints = _atr.Result.LastValue * AtrMulSL;
            double slDistancePips = slDistancePoints / Symbol.PipSize;
            double tpDistancePips = slDistancePips * RrRatio;

            double volume = CalculateVolumeInUnits(slDistancePips);

            // Circuit breaker against insufficient risk allowance
            if (volume < Symbol.VolumeInUnitsMin)
            {
                Print("Calculated volume {0} is below broker minimum {1}. Trade aborted.", volume, Symbol.VolumeInUnitsMin);
                return;
            }

            // Execute the market order natively with embedded SL and TP
            ExecuteMarketOrder(tradeType, SymbolName, volume, "Pro MM Scalp", slDistancePips, tpDistancePips);
        }

        protected override void OnTick()
        {
            // Insert your custom entry/momentum triggers here
            // Example:
            // if (SignalBuy && Positions.Count == 0) ExecuteTrade(TradeType.Buy);
        }
    }
}

Re: My perspective on scalping – Forex for scaling capital, Stocks for steady cash flow

Posted: Sun Sep 06, 2026 2:02 pm
by PTScalper
⚙️ Why cTrader excels with this specific logic

Symbol.PipValue Elegance: In MQL4/5, determining exact monetary risk requires calculating point-to-tick ratios using MODE_TICKVALUE and manually adjusting for contract sizes. cTrader's Symbol.PipValue abstracts all of this natively, meaning this exact cBot can be dragged from EURUSD to XAGUSD and the dollar-risk logic remains completely identical.

Symbol.NormalizeVolumeInUnits: High-volume automated strategies frequently hit Invalid Volume server rejections when math outputs a number like 105,420 units, but the broker's step size is 1,000. Using RoundingMode.Down instantly truncates the calculated volume to the highest permitted tier that keeps you exactly within your risk tolerance.