Why this shines specifically on cTrader:
The Math is Cleaner: Notice the CalculateVolume function. MetaTrader requires complex tick-value math, whereas cTrader's API has Symbol.PipValue naturally built to measure the value per individual unit.
OnBar() native function: cTrader has a native OnBar() method that only triggers when a 5-minute candle officially closes. This saves system resources because we don't have to constantly check if the timestamp changed like we do in MT4/MT5. We reserve OnTick() solely for trailing the breakeven Stop Loss.
Micro-Lot Precision: By using Symbol.NormalizeVolumeInUnits(..., RoundingMode.Down), the bot flawlessly adjusts your position size whether you are trading Forex, Crypto, or Indices, matching your exact 1% risk rule to your broker's specific fractional lot rules.
The 5-Minute Chart: The Scalper's Middle Ground
Re: The 5-Minute Chart: The Scalper's Middle Ground
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: The 5-Minute Chart: The Scalper's Middle Ground
To take a cTrader bot from "functional" to Prop Firm / Institutional Grade, it needs defensive engineering. Professional algorithms don't just look for entries; they actively protect capital against news spikes, weekend gaps, and emotional tilt.
Here is the Prop Firm Grade cBot. I have added four major institutional mechanics:
Prop Firm Equity Guardrails: Automatically halts trading for the day if a Daily Max Loss or Daily Profit Target is hit. This is mandatory if you are trading FTMO, MyFundedFX, or other prop firm capital.
Spread Filter: Prevents the bot from entering during heavy news events or illiquid periods where the spread violently widens.
Dynamic Trailing Stop (The Runner): After the first half of the position takes profit (TP1), the bot doesn't just move to breakeven and pray it hits TP2. It actively trails the runner using the ATR, locking in profit tick-by-tick as the trend continues.
Friday Auto-Close (No Weekend Hold): Automatically closes all open positions at a specified hour on Friday to completely eliminate weekend gap risk.
Here is the Prop Firm Grade cBot. I have added four major institutional mechanics:
Prop Firm Equity Guardrails: Automatically halts trading for the day if a Daily Max Loss or Daily Profit Target is hit. This is mandatory if you are trading FTMO, MyFundedFX, or other prop firm capital.
Spread Filter: Prevents the bot from entering during heavy news events or illiquid periods where the spread violently widens.
Dynamic Trailing Stop (The Runner): After the first half of the position takes profit (TP1), the bot doesn't just move to breakeven and pray it hits TP2. It actively trails the runner using the ATR, locking in profit tick-by-tick as the trend continues.
Friday Auto-Close (No Weekend Hold): Automatically closes all open positions at a specified hour on Friday to completely eliminate weekend gap risk.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: The 5-Minute Chart: The Scalper's Middle Ground
The Prop-Grade cTrader cBot (C#)
Code: Select all
using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class Pro5MinScalper_Institutional : Robot
{
// =========================================================================
// 1. RISK & PROP FIRM COMPLIANCE
// =========================================================================
[Parameter("Risk Per Trade (%)", DefaultValue = 1.0, Group = "1. Risk Management")]
public double RiskPercent { get; set; }
[Parameter("Daily Max Loss (%)", DefaultValue = 4.0, Group = "1. Risk Management",
DefaultValue = "Halts trading if daily drawdown hits this % (Prop Firm Guardrail)")]
public double MaxDailyLossPct { get; set; }
[Parameter("Daily Target (%)", DefaultValue = 6.0, Group = "1. Risk Management",
DefaultValue = "Halts trading and secures profits if daily target is hit")]
public double DailyTargetPct { get; set; }
// =========================================================================
// 2. TRADE TARGETS & TRAILING
// =========================================================================
[Parameter("ATR Period", DefaultValue = 14, Group = "2. Targets & Trailing")]
public int AtrPeriod { get; set; }
[Parameter("Stop Loss (ATR Multiplier)", DefaultValue = 1.5, Group = "2. Targets & Trailing")]
public double SlMultiplier { get; set; }
[Parameter("TP1 Scale Out (ATR Multiplier)", DefaultValue = 1.5, Group = "2. Targets & Trailing")]
public double Tp1Multiplier { get; set; }
[Parameter("TP2 Runner (ATR Multiplier)", DefaultValue = 3.0, Group = "2. Targets & Trailing")]
public double Tp2Multiplier { get; set; }
[Parameter("Trail Runner with ATR?", DefaultValue = true, Group = "2. Targets & Trailing")]
public bool UseTrailingStop { get; set; }
// =========================================================================
// 3. ENTRY FILTERS (CHOP & SPREAD)
// =========================================================================
[Parameter("Max Spread (Pips)", DefaultValue = 1.5, Group = "3. Filters")]
public double MaxSpreadPips { get; set; }
[Parameter("Fast EMA", DefaultValue = 9, Group = "3. Filters")]
public int FastEmaPeriod { get; set; }
[Parameter("Slow EMA", DefaultValue = 21, Group = "3. Filters")]
public int SlowEmaPeriod { get; set; }
[Parameter("Macro Trend (200 EMA)", DefaultValue = 200, Group = "3. Filters")]
public int TrendEmaPeriod { get; set; }
[Parameter("ADX Minimum (Chop Filter)", DefaultValue = 20.0, Group = "3. Filters")]
public double AdxThreshold { get; set; }
// =========================================================================
// 4. SESSION & TIME LIMITS
// =========================================================================
[Parameter("Start Hour (Server Time)", DefaultValue = 9, Group = "4. Time Management")]
public int StartHour { get; set; }
[Parameter("End Hour (Server Time)", DefaultValue = 15, Group = "4. Time Management")]
public int EndHour { get; set; }
[Parameter("Close Trades Friday Night?", DefaultValue = true, Group = "4. Time Management")]
public bool AvoidWeekendGaps { get; set; }
[Parameter("Friday Close Hour", DefaultValue = 20, Group = "4. Time Management")]
public int FridayCloseHour { get; set; }
// --- Internal Variables ---
private ExponentialMovingAverage _fastEma, _slowEma, _trendEma;
private DirectionalMovementSystem _adx;
private AverageTrueRange _atr;
private const string BotLabel = "Inst_5Min_Scalp";
private double _startOfDayBalance;
private DateTime _currentDay;
private bool _tradingHaltedToday = false;
// =========================================================================
// INITIALIZATION
// =========================================================================
protected override void OnStart()
{
_fastEma = Indicators.ExponentialMovingAverage(Bars.ClosePrices, FastEmaPeriod);
_slowEma = Indicators.ExponentialMovingAverage(Bars.ClosePrices, SlowEmaPeriod);
_trendEma = Indicators.ExponentialMovingAverage(Bars.ClosePrices, TrendEmaPeriod);
_adx = Indicators.DirectionalMovementSystem(14);
_atr = Indicators.AverageTrueRange(AtrPeriod, MovingAverageType.Simple);
_currentDay = Server.Time.Date;
_startOfDayBalance = Account.Balance;
}
// =========================================================================
// TICK LOGIC (Trailing, Equity Guardrails, Weekend Close)
// =========================================================================
protected override void OnTick()
{
CheckDailyReset();
CheckEquityGuardrails();
CheckFridayClose();
var openPositions = Positions.FindAll(BotLabel, SymbolName);
if (openPositions.Length == 0) return;
double currentAtr = _atr.Result.Last(1);
double tp1Distance = currentAtr * Tp1Multiplier;
double trailingDist = currentAtr * SlMultiplier;
foreach (var pos in openPositions)
{
if (pos.TradeType == TradeType.Buy)
{
// Breakeven + ATR Trailing Stop
if (Symbol.Bid >= pos.EntryPrice + tp1Distance)
{
double newStop = UseTrailingStop ? (Symbol.Bid - trailingDist) : pos.EntryPrice;
newStop = Math.Max(newStop, pos.EntryPrice); // Never trail worse than breakeven
if (newStop - pos.StopLoss > 2 * Symbol.TickSize || pos.StopLoss == null)
ModifyPositionAsync(pos, Math.Round(newStop, Symbol.Digits), pos.TakeProfit);
}
}
else if (pos.TradeType == TradeType.Sell)
{
// Breakeven + ATR Trailing Stop
if (Symbol.Ask <= pos.EntryPrice - tp1Distance)
{
double newStop = UseTrailingStop ? (Symbol.Ask + trailingDist) : pos.EntryPrice;
newStop = Math.Min(newStop, pos.EntryPrice); // Never trail worse than breakeven
if (pos.StopLoss - newStop > 2 * Symbol.TickSize || pos.StopLoss == null)
ModifyPositionAsync(pos, Math.Round(newStop, Symbol.Digits), pos.TakeProfit);
}
}
}
}
// =========================================================================
// ENTRY LOGIC (Bar Close)
// =========================================================================
protected override void OnBar()
{
if (_tradingHaltedToday || Positions.FindAll(BotLabel, SymbolName).Length > 0) return;
if (!IsTradingTime()) return;
if ((Symbol.Spread / Symbol.PipSize) > MaxSpreadPips) return; // News / Spread Spike Protection
int closedBar = Bars.Count - 2;
int prevBar = Bars.Count - 3;
bool buySignal = _fastEma.Result[closedBar] > _slowEma.Result[closedBar] &&
_fastEma.Result[prevBar] <= _slowEma.Result[prevBar] &&
Bars.ClosePrices[closedBar] > _trendEma.Result[closedBar] &&
_adx.ADX[closedBar] > AdxThreshold;
bool sellSignal = _fastEma.Result[closedBar] < _slowEma.Result[closedBar] &&
_fastEma.Result[prevBar] >= _slowEma.Result[prevBar] &&
Bars.ClosePrices[closedBar] < _trendEma.Result[closedBar] &&
_adx.ADX[closedBar] > AdxThreshold;
if (buySignal) ExecuteSplitTrade(TradeType.Buy);
if (sellSignal) ExecuteSplitTrade(TradeType.Sell);
}
// =========================================================================
// TRADE EXECUTION & SIZING
// =========================================================================
private void ExecuteSplitTrade(TradeType direction)
{
double atr = _atr.Result.Last(1);
double slPips = (atr * SlMultiplier) / Symbol.PipSize;
double tp1Pips = (atr * Tp1Multiplier) / Symbol.PipSize;
double tp2Pips = (atr * Tp2Multiplier) / Symbol.PipSize;
double exactVolume = (Account.Equity * (RiskPercent / 100.0)) / (slPips * Symbol.PipValue);
double halfVolume = Symbol.NormalizeVolumeInUnits(exactVolume / 2.0, RoundingMode.Down);
if (halfVolume < Symbol.VolumeInUnitsMin) return; // Don't trade if account is too small for risk rules
ExecuteMarketOrder(direction, SymbolName, halfVolume, BotLabel, slPips, tp1Pips);
ExecuteMarketOrder(direction, SymbolName, halfVolume, BotLabel, slPips, tp2Pips);
}
// =========================================================================
// SAFETY & TIME MECHANICS
// =========================================================================
private void CheckDailyReset()
{
if (Server.Time.Date != _currentDay.Date)
{
_currentDay = Server.Time.Date;
_startOfDayBalance = Account.Balance;
_tradingHaltedToday = false;
Print("New trading day. Equity guardrails reset.");
}
}
private void CheckEquityGuardrails()
{
if (_tradingHaltedToday) return;
double dailyPnL = Account.Equity - _startOfDayBalance;
double dailyReturnPct = (dailyPnL / _startOfDayBalance) * 100;
if (dailyReturnPct <= -MaxDailyLossPct)
{
Print("🚨 MAXIMUM DAILY LOSS HIT. Halting trading to protect account.");
HaltTrading();
}
else if (dailyReturnPct >= DailyTargetPct)
{
Print("🏆 DAILY PROFIT TARGET HIT. Securing profits and halting trading.");
HaltTrading();
}
}
private void CheckFridayClose()
{
if (AvoidWeekendGaps && Server.Time.DayOfWeek == DayOfWeek.Friday && Server.Time.Hour >= FridayCloseHour)
{
Print("Weekend approaching. Closing all positions.");
HaltTrading();
}
}
private void HaltTrading()
{
_tradingHaltedToday = true;
foreach (var pos in Positions.FindAll(BotLabel, SymbolName))
{
ClosePositionAsync(pos);
}
}
private bool IsTradingTime()
{
int currentMins = Server.Time.Hour * 60 + Server.Time.Minute;
int startMins = StartHour * 60 + StartMinute;
int endMins = EndHour * 60 + EndMinute;
return (currentMins >= startMins && currentMins <= endMins);
}
}
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: The 5-Minute Chart: The Scalper's Middle Ground
Pro-Desk Features Explained:
The CheckEquityGuardrails() Function: This is the most important addition. By default, it tracks your equity tick-by-tick. If you lose 4% in a single day, it will instantly close all open trades and physically block the bot from taking any more entries until midnight server time. This prevents emotional "revenge trading" spirals—the number one cause of failed prop firm evaluations.
The Spread Filter (MaxSpreadPips): During events like NFP or CPI, brokers will widen the spread from 0.5 pips to 10+ pips. If the bot's signal fires during this time, a normal bot gets entered at a terrible price and instantly stopped out. This code checks (Symbol.Spread / Symbol.PipSize) > MaxSpreadPips on the very frame the entry is triggered, and aborts the trade if the broker is quoting an abusive spread.
UseTrailingStop (ATR Trailing): Once the trade reaches a 1:1.5 Risk-to-Reward, it cashes out 50%. Previously, we just moved the remainder to breakeven. Now, as the price pushes in your favor, the stop loss dynamically follows it at a safe ATR distance. You will catch massive, unexpected trend continuations with zero risk.
Asynchronous Execution (ModifyPositionAsync / ClosePositionAsync): Notice the Async tag on position modifications. Normal retail bots wait for the broker to confirm a modification before moving to the next line of code (which causes a bottleneck). Asynchronous execution fires the modification commands in milliseconds to the server, allowing the bot to handle multiple fast-moving ticks instantly.
The CheckEquityGuardrails() Function: This is the most important addition. By default, it tracks your equity tick-by-tick. If you lose 4% in a single day, it will instantly close all open trades and physically block the bot from taking any more entries until midnight server time. This prevents emotional "revenge trading" spirals—the number one cause of failed prop firm evaluations.
The Spread Filter (MaxSpreadPips): During events like NFP or CPI, brokers will widen the spread from 0.5 pips to 10+ pips. If the bot's signal fires during this time, a normal bot gets entered at a terrible price and instantly stopped out. This code checks (Symbol.Spread / Symbol.PipSize) > MaxSpreadPips on the very frame the entry is triggered, and aborts the trade if the broker is quoting an abusive spread.
UseTrailingStop (ATR Trailing): Once the trade reaches a 1:1.5 Risk-to-Reward, it cashes out 50%. Previously, we just moved the remainder to breakeven. Now, as the price pushes in your favor, the stop loss dynamically follows it at a safe ATR distance. You will catch massive, unexpected trend continuations with zero risk.
Asynchronous Execution (ModifyPositionAsync / ClosePositionAsync): Notice the Async tag on position modifications. Normal retail bots wait for the broker to confirm a modification before moving to the next line of code (which causes a bottleneck). Asynchronous execution fires the modification commands in milliseconds to the server, allowing the bot to handle multiple fast-moving ticks instantly.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.