Advertisement
Master exponential money management, position sizing calculators, strict daily stop-loss limits, and overcoming FOMO on micro-timeframes.
FTtrader
Posts: 954 Joined: Mon Aug 03, 2026 2:43 pm
Post
by FTtrader » Sat Sep 26, 2026 2:23 pm
2. Pro cTrader cBot (C#): Event-Driven Risk Enforcer
If you are executing manually or running algorithmic strategies directly on your broker infrastructure, scanning a loop on every tick wastes CPU cycles. A professional C# implementation hooks into the Positions.Opened event and processes execution asynchronously.
This cBot runs in the background. If you manually open a trade without a stop loss, it instantly calculates a structural stop using an ATR buffer and applies it asynchronously to avoid locking up the main thread.
Code: Select all
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using System;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class ProRiskEnforcer : Robot
{
[Parameter("ATR Period", DefaultValue = 14, Group = "Risk Structure")]
public int AtrPeriod { get; set; }
[Parameter("ATR Multiplier", DefaultValue = 1.5, Group = "Risk Structure")]
public double AtrMultiplier { get; set; }
private AverageTrueRange _atr;
protected override void OnStart()
{
// Initialize ATR for dynamic volatility buffering
_atr = Indicators.AverageTrueRange(MarketData.GetBars(TimeFrame.Minute15), AtrPeriod, MovingAverageType.Exponential);
// Subscribe only to relevant events (highly efficient)
Positions.Opened += OnPositionOpened;
Print("Pro Risk Enforcer initialized. Monitoring for naked positions.");
}
private void OnPositionOpened(PositionOpenedEventArgs args)
{
var position = args.Position;
// Immediately check if the order lacks a Stop Loss
if (!position.StopLoss.HasValue)
{
Print($"[ALERT] Naked position detected on {position.SymbolName} (Ticket: {position.Id}). Calculating structural SL.");
ApplyStructuralStopLoss(position);
}
}
private void ApplyStructuralStopLoss(Position position)
{
var bars = MarketData.GetBars(TimeFrame.Minute15);
double currentAtr = _atr.Result.Last(1);
double buffer = currentAtr * AtrMultiplier;
double? newStopLoss = null;
if (position.TradeType == TradeType.Buy)
{
// Find recent swing low over the last 15 periods
double swingLow = bars.LowPrices.Minimum(15);
newStopLoss = swingLow - buffer;
}
else if (position.TradeType == TradeType.Sell)
{
// Find recent swing high over the last 15 periods
double swingHigh = bars.HighPrices.Maximum(15);
newStopLoss = swingHigh + buffer;
}
if (newStopLoss.HasValue)
{
// Execute modification asynchronously so the bot's thread isn't blocked
ModifyPositionAsync(position, newStopLoss, position.TakeProfit, hasError =>
{
if (hasError.IsSuccessful)
Print($"[SUCCESS] Structural SL applied to {position.Id} at {Math.Round(newStopLoss.Value, 5)}");
else
Print($"[ERROR] Failed to apply SL to {position.Id}: {hasError.Error}");
});
}
}
}
}
Recommended broker for automated trading & scalping
FTtrader
Posts: 954 Joined: Mon Aug 03, 2026 2:43 pm
Post
by FTtrader » Sat Sep 26, 2026 2:24 pm
Here are the professional-grade implementations for both MetaTrader 5 (MQL5) and MetaTrader 4 (MQL4).
To make these truly "Pro", they include several critical safeguards that separate amateur scripts from institutional utilities:
StopLevel Protection: Brokers have minimum stop distances. If the calculated structural stop is too close to the current price, the script automatically adjusts it to the broker's minimum allowable distance to prevent Error 130 (Invalid Stops) rejections.
Completed Candle Referencing: The structure array begins at candle index 1 (the last closed candle). This ensures it doesn't calculate a stop based on the currently forming, fluctuating candle.
Digit Normalization: Prices are rounded precisely to your broker’s pip decimal settings (3-digit or 5-digit) to prevent formatting errors.
FTtrader
Posts: 954 Joined: Mon Aug 03, 2026 2:43 pm
Post
by FTtrader » Sat Sep 26, 2026 2:25 pm
Run these as an Expert Advisor (EA) on the chart you are actively trading. It will quietly monitor your manual entries in the background and instantly bolt a structural stop onto any naked position it finds.
MetaTrader 5 (MQL5) Pro Risk Enforcer
MT5 separates "Orders" (pending) from "Positions" (active trades). This EA iterates strictly through your active positions on the current chart symbol.
Code: Select all
//+------------------------------------------------------------------+
//| ProRiskEnforcer_MT5.mq5 |
//| |
//+------------------------------------------------------------------+
#property strict
#include <Trade\Trade.mqh>
input int InpLookback = 15; // Structure Lookback (Bars)
input bool InpUseAtr = true; // Use ATR Volatility Buffer?
input int InpAtrPeriod = 14; // ATR Period
input double InpAtrMultiplier= 1.5; // ATR Multiplier
CTrade trade;
int atrHandle;
int OnInit()
{
// Initialize the ATR indicator handle
atrHandle = iATR(_Symbol, _Period, InpAtrPeriod);
if(atrHandle == INVALID_HANDLE)
{
Print("Failed to create ATR handle");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
void OnTick()
{
// Scan all open positions
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
// Only manage positions for the chart's active symbol
if(ticket > 0 && PositionGetString(POSITION_SYMBOL) == _Symbol)
{
double sl = PositionGetDouble(POSITION_SL);
// If the position has no Stop Loss (0.0), calculate and apply one
if(sl == 0.0)
{
ApplyStructuralSL(ticket);
}
}
}
}
void ApplyStructuralSL(ulong ticket)
{
long type = PositionGetInteger(POSITION_TYPE);
double tp = PositionGetDouble(POSITION_TP);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
double stoplevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * point;
// Calculate ATR Buffer
double buffer = 0.0;
if(InpUseAtr)
{
double atrArray[1];
if(CopyBuffer(atrHandle, 0, 1, 1, atrArray) > 0)
buffer = atrArray[0] * InpAtrMultiplier;
}
else buffer = 10 * point; // Fallback hard buffer
double newSL = 0.0;
if(type == POSITION_TYPE_BUY)
{
double lowArray[];
ArraySetAsSeries(lowArray, true);
if(CopyLow(_Symbol, _Period, 1, InpLookback, lowArray) > 0)
{
double swingLow = lowArray[ArrayMinimum(lowArray)];
newSL = swingLow - buffer;
// Protect against Broker StopLevels
double minSL = SymbolInfoDouble(_Symbol, SYMBOL_BID) - stoplevel;
if(newSL > minSL) newSL = minSL;
}
}
else if(type == POSITION_TYPE_SELL)
{
double highArray[];
ArraySetAsSeries(highArray, true);
if(CopyHigh(_Symbol, _Period, 1, InpLookback, highArray) > 0)
{
double swingHigh = highArray[ArrayMaximum(highArray)];
newSL = swingHigh + buffer;
// Protect against Broker StopLevels
double minSL = SymbolInfoDouble(_Symbol, SYMBOL_ASK) + stoplevel;
if(newSL < minSL) newSL = minSL;
}
}
if(newSL > 0.0)
{
newSL = NormalizeDouble(newSL, digits);
if(trade.PositionModify(ticket, newSL, tp))
Print("[SUCCESS] Structural SL applied to ticket ", ticket, " at ", newSL);
else
Print("[ERROR] Failed to apply SL to ticket ", ticket, " Code: ", GetLastError());
}
}
FTtrader
Posts: 954 Joined: Mon Aug 03, 2026 2:43 pm
Post
by FTtrader » Sat Sep 26, 2026 2:25 pm
MetaTrader 4 (MQL4) Pro Risk Enforcer
MT4 uses a legacy pool system where all trades (active and pending) are grouped as "Orders". This script selects open market orders sequentially, checks if they belong to the current chart, and verifies their stop status.
Code: Select all
//+------------------------------------------------------------------+
//| ProRiskEnforcer_MT4.mq4 |
//| |
//+------------------------------------------------------------------+
#property strict
extern int InpLookback = 15; // Structure Lookback (Bars)
extern bool InpUseAtr = true; // Use ATR Volatility Buffer?
extern int InpAtrPeriod = 14; // ATR Period
extern double InpAtrMultiplier= 1.5; // ATR Multiplier
int OnInit()
{
return(INIT_SUCCEEDED);
}
void OnTick()
{
// Iterate over all active orders in the terminal
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
// Filter for current chart symbol and market execution (Buy/Sell)
if(OrderSymbol() == Symbol() && (OrderType() == OP_BUY || OrderType() == OP_SELL))
{
// If StopLoss is zero (naked), calculate and apply
if(OrderStopLoss() == 0.0)
{
ApplyStructuralSL();
}
}
}
}
}
void ApplyStructuralSL()
{
int ticket = OrderTicket();
int type = OrderType();
double tp = OrderTakeProfit();
double openPrice = OrderOpenPrice();
double stoplevel = MarketInfo(Symbol(), MODE_STOPLEVEL) * Point;
// Calculate ATR Buffer
double buffer = 0.0;
if(InpUseAtr)
buffer = iATR(Symbol(), 0, InpAtrPeriod, 1) * InpAtrMultiplier;
else
buffer = 10 * Point; // Fallback hard buffer
double newSL = 0.0;
if(type == OP_BUY)
{
// Find the lowest low index starting from candle 1 (closed candle)
int lowestIndex = iLowest(Symbol(), 0, MODE_LOW, InpLookback, 1);
double swingLow = Low[lowestIndex];
newSL = swingLow - buffer;
// Protect against Broker StopLevels
double minSL = Bid - stoplevel;
if(newSL > minSL) newSL = minSL - Point;
}
else if(type == OP_SELL)
{
// Find the highest high index starting from candle 1
int highestIndex = iHighest(Symbol(), 0, MODE_HIGH, InpLookback, 1);
double swingHigh = High[highestIndex];
newSL = swingHigh + buffer;
// Protect against Broker StopLevels
double minSL = Ask + stoplevel;
if(newSL < minSL) newSL = minSL + Point;
}
if(newSL > 0.0)
{
// Sanitize digits before server request
newSL = NormalizeDouble(newSL, Digits);
// Modify order
bool res = OrderModify(ticket, openPrice, newSL, tp, 0, clrNONE);
if(res)
Print("[SUCCESS] Structural SL applied to ticket ", ticket, " at ", newSL);
else
Print("[ERROR] Failed to apply SL to ticket ", ticket, " Code: ", GetLastError());
}
}