top of page

RiskGuard Strategy
Specification Config. 2

Versione B2.01 del 15/07/2026 BETA

This page contains the complete technical specification required to create indicators compatible with RiskGuard Config 2.

The only supported method for sending signals is the SendRiskGuardConfig2Signal() function.

Purpose of This Page
 

This page defines the mandatory rules for creating an MQL5 indicator compatible with RiskGuard Strategy Mode.

This document is intended for:

  • MQL5 developers

  • Artificial Intelligence systems

  • Automatic code generators

  • Developers creating RiskGuard-compatible strategies

Whenever a rule is marked as mandatory, it must be followed without exception.

The sole purpose of the indicator is to generate trading signals.

All trade execution and risk management are handled entirely by RiskGuard.

 

What the Indicator Must Do
 

The indicator must:

  • Analyze the market

  • Generate trading signals

  • Calculate the Stop Loss

  • Calculate the Take Profit

  • Optionally calculate the price of a pending order

  • Send the signal to RiskGuard using the official SendRiskGuardConfig2Signal() function

What the Indicator Must Not Do
 

The indicator must not:

  • Open trades

  • Close trades

  • Modify trades

  • Calculate the lot size

  • Manage risk

  • Manage Break Even

  • Manage partial Take Profits

  • Manage the Trailing Stop

  • Manage daily loss limits

  • Manage Quantum

All of these functions are handled exclusively by RiskGuard.

 

Indicator Name
 

The indicator name must begin with:

RiskGuard_

Valid examples:

  • RiskGuard_ORB

  • RiskGuard_MACD

  • RiskGuard_Stochastic

  • RiskGuard_Breakout

Invalid examples:

  • ORB

  • MACD

  • RG_ORB

  • AutoStrategy

RiskGuard may automatically reject indicators whose names do not begin with the RiskGuard_ prefix.

 

Chart Timeframe Independence
 

RiskGuard is designed to operate with one chart window for each symbol and to be saved as the default template.

The user may freely change the chart timeframe at any time.

The indicator must ensure that changing the chart timeframe does not affect the strategy’s behavior.

Changing the timeframe must not:

  • Modify the strategy logic

  • Modify the generated signals

  • Generate new signals

  • Generate duplicate signals

  • Generate retroactive signals

If the strategy uses one or more specific timeframes, they must be defined internally by the indicator.

The strategy logic must not depend on the timeframe currently displayed on the chart.

 

No Retroactive Signals
 

The indicator must generate signals exclusively in real time.

If a trading condition occurred before the indicator was loaded, the signal must be ignored.

Example:

Breakout at 11:00.

Indicator loaded at 15:00.

Correct behavior:

No signal.

Incorrect behavior:

Generating a signal at 15:00 for the breakout that occurred at 11:00.

 

No Duplicate Signals
 

The same signal must never be sent more than once.

The indicator developer is fully responsible for preventing duplicate signals.

The indicator must ensure that:

  • Refreshing the chart does not generate duplicate signals

  • Changing the chart timeframe does not generate duplicate signals

  • Reinitializing the indicator does not generate duplicate signals

  • The same trading event is never sent more than once

No Trade Execution Functions
 

A RiskGuard-compatible indicator must not execute trading operations directly.

Functions such as the following must not be used:

  • OrderSend()

  • Buy()

  • Sell()

  • PositionOpen()

  • PositionClose()

  • PositionModify()

RiskGuard is the only component authorized to execute trades.

 

Handling Indicator Removal
 

When a RiskGuard-compatible indicator is removed from the chart, it is recommended to properly clean up all resources created by the indicator.

For example:

  • Indicator-specific Global Variables

  • Graphical objects

  • Boxes

  • Lines

  • Labels

  • Persistent state variables

This helps reduce the risk of:

  • Duplicate signals

  • Residual signals

  • Objects left on the chart

  • Unexpected behavior when the indicator is reloaded

It is recommended to implement this cleanup logic inside:

OnDeinit()

Particular attention should be given to the following case:

REASON_CHARTCHANGE

MetaTrader reinitializes the indicator whenever the chart timeframe is changed.

In many cases, deleting all variables during REASON_CHARTCHANGE may cause duplicate signals after the indicator is reloaded.

For this reason, the cleanup logic should be carefully designed according to the behavior of the strategy.

Example:

void OnDeinit(const int reason)

  {

   if(reason == REASON_CHARTCHANGE)

      return;

      GlobalVariableDel(LastSignalGVName());
      ObjectDelete(0, "MyStrategyBox");
      ObjectDelete(0, "MyStrategyLine");

  }

The cleanup logic depends entirely on the behavior of the strategy.

Not all indicators require the same cleanup logic in OnDeinit().

The developer is responsible for implementing a cleanup strategy that is consistent with the behavior of their own trading strategy.

Official SendRiskGuardConfig2Signal() Function
 

WARNING

The SendRiskGuardConfig2Signal() function provided on this page is an integral part of the official RiskGuard protocol.

The function must be copied in full without any modifications.

The following are not permitted:

  • Modifying the code

  • Optimizing the code

  • Refactoring the code

  • Rewriting the code

  • Replacing instructions with equivalent instructions

  • Adding instructions

  • Removing instructions

The function must be reproduced exactly, character for character.

Any modification makes the indicator non-compliant with the RiskGuard specifications.

//+------------------------------------------------------------------+

//| Official RiskGuard signal function - Config. 2                   |

//|                                                                  |

//| pendingPrice = 0.0  -> market order                              |

//| pendingPrice > 0.0  -> pending order                             |

//|                                                                  |

//| DO NOT MODIFY THIS FUNCTION.                                     |

//| This function must remain identical in all RiskGuard-compatible  |

//| indicators.                                                      |

//+------------------------------------------------------------------+

void SendRiskGuardConfig2Signal(double stopPrice,

                         double takePrice,

                         double pendingPrice = 0.0)

  {

   //--- Always delete old RiskGuard variables first

   GlobalVariableDel("RG_Strategy_Config2_Symbol_" + _Symbol);

   GlobalVariableDel("RG_Strategy_Config2_Stop_"   + _Symbol);

   GlobalVariableDel("RG_Strategy_Config2_Take_"   + _Symbol);

   GlobalVariableDel("RG_Strategy_Config2_Price_"  + _Symbol);

 

   //--- Normalize prices

   stopPrice = NormalizeDouble(stopPrice, _Digits);

   takePrice = NormalizeDouble(takePrice, _Digits);

 

   if(pendingPrice > 0.0)

      pendingPrice = NormalizeDouble(pendingPrice, _Digits);

 

   bool isPending = (pendingPrice > 0.0);

 

   //--- Create variables in the correct order

   GlobalVariableSet("RG_Strategy_Config2_Stop_" + _Symbol, stopPrice);

   GlobalVariableSet("RG_Strategy_Config2_Take_" + _Symbol, takePrice);

 

   if(isPending)

      GlobalVariableSet("RG_Strategy_Config2_Price_" + _Symbol, pendingPrice);

 

   //--- RG_Strategy_Symbol must always be created last

   GlobalVariableSet("RG_Strategy_Config2_Symbol_" + _Symbol, 1);

 

   //--- Log

   if(isPending)

     {

      Print("[", MQLInfoString(MQL_PROGRAM_NAME), "][INDICATOR][SIGNAL] Pending signal variables created",

            " | Symbol=", _Symbol,

            " | Stop=", DoubleToString(stopPrice, _Digits),

            " | Take=", DoubleToString(takePrice, _Digits),

            " | Price=", DoubleToString(pendingPrice, _Digits),

            " | Bid=", DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits),

            " | Ask=", DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits));

     }

   else

     {

      Print("[", MQLInfoString(MQL_PROGRAM_NAME), "][INDICATOR][SIGNAL] Market signal variables created",

            " | Symbol=", _Symbol,

            " | Stop=", DoubleToString(stopPrice, _Digits),

            " | Take=", DoubleToString(takePrice, _Digits),

            " | Bid=", DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits),

            " | Ask=", DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits));

     }

  }

Sending a Market Order Signal
 

Example:

 

SendRiskGuardConfig2Signal(stopPrice, takePrice);

 

Sending a Pending Order Signal
 

Example:

 

SendRiskGuardConfig2Signal(stopPrice, takePrice, pendingPrice);

 

What RiskGuard Does After Receiving a Signal
 

After receiving a signal, RiskGuard can:

  • Validate the prices

  • Automatically determine whether to open a BUY or SELL trade

  • Calculate the lot size

  • Calculate the risk

  • Apply Quantum

  • Apply exposure limits

  • Apply Break Even

  • Apply partial Take Profits

  • Apply the Trailing Stop

  • Open market orders

  • Open pending orders

  • Manage open positions

The indicator is not responsible for any of these operations.

 

Magic Numbers Used by RiskGuard
 

When RiskGuard opens trades through Strategy Mode – CONFIG. 2, it always uses the following fixed Magic Number:

30032999

This applies to both:

  • Market orders

  • Pending orders

opened in response to a signal generated by a RiskGuard-compatible indicator.

This allows the indicator developer to easily identify orders and positions generated by Strategy Mode.

For example, an indicator can check whether there are open positions or pending orders created by RiskGuard Strategy by filtering the following Magic Number:

30032999

All other trades opened directly by RiskGuard (outside of Strategy Mode) use Magic Numbers within the reserved RiskGuard range.

Therefore, if a developer wants to monitor only the activity generated by Strategy Mode – CONFIG. 2, they should filter exclusively for the following Magic Number:

30032999

 

Responsibility and Support
 

The indicator and RiskGuard are separate components.

RiskGuard has no knowledge of the logic used by the indicator to generate a signal.

After receiving a valid signal, RiskGuard behaves exactly as if the user had clicked the CALC button on the panel, manually entered the prices, and then clicked the OPEN button.

The only information received by RiskGuard is:

  • Entry price (for pending orders only)

  • Stop Loss

  • Take Profit

RiskGuard does not receive any information about the strategy logic.

 

Technical Support
 

RiskGuard provides technical support exclusively for:

  • RiskGuard installation

  • RiskGuard configuration

  • RiskGuard features and functionality

  • Issues related to RiskGuard

RiskGuard does not provide free technical support for:

  • Strategies developed by third parties

  • Indicators developed by third parties

  • Code generated using Artificial Intelligence

  • User-requested custom modifications

For any issues related to the strategy logic or the operation of an indicator, the user must contact the indicator developer directly.

Signal Validation
 

Receiving a signal does not guarantee that a trade will be opened.

Before opening a trade, RiskGuard performs all of its standard validation checks.

For example:

  • Stop Loss & DD Block

  • Exposure limits

  • Price validation

  • Risk validation

  • Other internal safety checks

If any of these checks prevent the trade from being opened, the signal is ignored, deleted, and no trade is executed.

Strategy Tester Compatibility
 

RiskGuard can be used within the MetaTrader Strategy Tester.

When RiskGuard loads an indicator through the following parameter:

StrategyIndicator

MetaTrader allows only the RiskGuard inputs to be modified.

The strategy indicator inputs cannot be accessed directly from the RiskGuard Strategy Tester window.

To allow the end user to modify the strategy inputs during testing, the developer must provide an .mq5 file containing at least the strategy input definitions.

Example:

input int SignalHour = 9;

input int SignalMinute = 0;

input int StopDistancePoints = 1000;

This allows the user to compile the indicator with the desired parameters and use it together with RiskGuard in the Strategy Tester.

 

Development and Backtesting Recommendations
 

To improve performance during backtesting, it is recommended to completely separate the strategy logic from its graphical representation.

Indicators, lines, moving averages, bands, levels, and all other graphical elements used by the strategy should be displayed:

  • During normal chart operation

  • During visual backtesting

When the backtest is run in non-visual mode, it is recommended not to create, update, or draw any graphical elements.

This approach provides several benefits:

  • Significantly faster backtesting

  • Reduced platform workload

  • Elimination of unnecessary graphical processing

  • Full visual support during development, strategy verification, and live trading

The signal generation logic should continue to operate normally even when the graphical components are disabled. Visual elements should therefore be used solely for display purposes and must not be required for generating strategy signals.

In MQL5, the execution mode can be detected using the program properties, for example:

 

bool showGraphics = true;

 

if(MQLInfoInteger(MQL_TESTER) &&

!MQLInfoInteger(MQL_VISUAL_MODE))

{

showGraphics = false;

}

 

The showGraphics variable can then be checked before creating graphical indicators, visible buffers, objects, lines, labels, or any other elements intended solely for visualization.

 

Example:

 

if(showGraphics)

{

// Creazione o aggiornamento degli elementi grafici

}

 

It is also recommended to:

  • Create the indicator handles required for calculations even during non-visual backtesting

  • Avoid adding them graphically to the chart when visual mode is not active

  • Avoid using graphical objects to store information required by the strategy

  • Keep the calculation logic, signal generation logic, and graphical logic separate

  • Avoid graphical updates on every tick when they are not actually necessary

During strategy development, it is recommended to initially perform several tests in visual mode, verifying that signals, entries, Stop Losses, and Take Profits are consistent with the elements displayed on the chart.

Once the strategy has been verified to operate correctly, longer tests can be performed in non-visual mode to achieve higher execution speed.

 

Protecting the Strategy Logic
 

The developer is free to choose how to distribute their strategy.

Both approaches are fully supported.

Fully Open Strategy

Indicator.mq5

Strategy with Protected Logic

Indicator.mq5 ProtectedLibrary.ex5

The .mq5 file may contain:

  • The strategy inputs

  • The SendRiskGuardConfig2Signal() function

  • The link to the protected library

The proprietary strategy logic can be kept inside one or more compiled libraries.

This allows the end user to modify the strategy inputs during testing without gaining access to the proprietary source code.

 

Use on Demo or Live Accounts
 

For normal use on a Demo or Live account, distributing the .mq5 file is not required.

The user can simply use the compiled file:

Indicator.ex5

and modify the strategy inputs directly from the indicator's properties on the chart.

 

Final Checklist
 

Before considering an indicator fully compatible with RiskGuard, verify that:

✓ Its name begins with RiskGuard_

✓ It does not use trade execution functions

✓ It does not generate retroactive signals

✓ It does not generate duplicate signals

✓ Its behavior does not change when the chart timeframe is changed

✓ It uses the SendRiskGuardConfig2Signal() function

✓ The SendRiskGuardConfig2Signal() function has not been modified

✓ Stop Loss and Take Profit are calculated by the indicator

✓ Risk management is handled entirely by RiskGuard

 

Example Indicators
 

This page includes two complete and fully functional example indicators.

Both fully comply with the official RiskGuard Strategy specifications and can be used as a reference for developing custom strategies.

The examples demonstrate:

  • How to structure a RiskGuard-compatible indicator

  • How to prevent duplicate signals

  • How to avoid retroactive signals

  • How to correctly send market and pending order signals

  •  

Example 1 — RiskGuard_auto.mq5
 

An extremely simple example indicator.

This indicator generates an automatic signal at a specific time defined by the user.

Its purpose is to demonstrate the minimum functionality required for a RiskGuard-compatible indicator.

Main features:

  • Generates a signal at a specific time

  • Supports market and pending orders

  • Automatically calculates the Stop Loss and Take Profit

  • Prevents duplicate signals

  • Prevents retroactive signals

  • Maintains consistent behavior when the chart timeframe changes

This example is recommended as a starting point for understanding the basic RiskGuard integration logic.

 

//+------------------------------------------------------------------+

//|                                           RiskGuard_auto.mq5     |

//+------------------------------------------------------------------+

//| RiskGuard Example Strategy Indicator                             |

//|                                                                  |

//| This indicator shows the minimum logic needed to send a trading  |

//| signal from a custom indicator to RiskGuard.                     |

//|                                                                  |

//| The example generates a signal at a specific broker/server time. |

//|                                                                  |

//| Developers can replace the example signal logic with their own   |

//| strategy while keeping SendRiskGuardConfig2Signal() unchanged.          |

//|                                                                  |

//| IMPORTANT                                                        |

//| SendRiskGuardConfig2Signal() is the official RiskGuard integration      |

//| function and should not be modified.                             |

//+------------------------------------------------------------------+

#property indicator_chart_window

#property indicator_plots 0

 

input int SignalHour          = 11;      // Broker/server hour when the signal must be generated

input int SignalMinute        = 0;       // Broker/server minute when the signal must be generated

input int StopDistancePoints  = 40000;   // Stop Loss distance from entry price, in points

input int TakeDistancePoints  = 40000;   // Take Profit distance from entry price, in points

input int PriceDistancePoints = 0;   // 0 = market order, >0 = pending order distance from current price, in points

 

 

//+------------------------------------------------------------------+

//| Last signal Global Variable name                                 |

//+------------------------------------------------------------------+

//| The Global Variable name includes:                               |

//| - RG prefix                                                      |

//| - indicator program name                                         |

//| - symbol                                                         |

//| - variable purpose                                               |

//|                                                                  |

//| This prevents conflicts when the same indicator is used on        |

//| multiple symbols or charts.                                      |

//+------------------------------------------------------------------+

string LastSignalGVName()

  {

   return "RG_" + MQLInfoString(MQL_PROGRAM_NAME) + "_" + _Symbol + "_LastSignal";

  }

 

 

//+------------------------------------------------------------------+

//| Check if a signal was already generated in this server minute    |

//+------------------------------------------------------------------+

//| MetaTrader reloads indicators when the chart timeframe changes.  |

//|                                                                  |

//| Without this protection, changing timeframe during the same      |

//| signal minute could generate the same RiskGuard signal again.    |

//|                                                                  |

//| The Global Variable stores TimeCurrent() when the signal is sent. |

//| The comparison ignores seconds and checks only:                  |

//| year, month, day, hour and minute.                               |

//|                                                                  |

//| This means:                                                      |

//| - 11:10:03 and 11:10:25 are treated as the same signal minute    |

//| - 11:10 and 11:45 are treated as different possible signals      |

//+------------------------------------------------------------------+

bool SignalAlreadySentThisMinute()

  {

   string gvName = LastSignalGVName();

 

   if(!GlobalVariableCheck(gvName))

      return false;

 

   datetime lastSignal = (datetime)GlobalVariableGet(gvName);

 

   MqlDateTime lastTm;

   MqlDateTime nowTm;

 

   TimeToStruct(lastSignal, lastTm);

   TimeToStruct(TimeCurrent(), nowTm);

 

   if(lastTm.year == nowTm.year &&

      lastTm.mon  == nowTm.mon  &&

      lastTm.day  == nowTm.day  &&

      lastTm.hour == nowTm.hour &&

      lastTm.min  == nowTm.min)

      return true;

 

   return false;

  }

 

 

//+------------------------------------------------------------------+

//| Save last signal timestamp                                       |

//+------------------------------------------------------------------+

//| The exact server timestamp is stored.                            |

//| Seconds are useful for logs/debugging, but duplicate protection  |

//| compares only up to the minute.                                  |

//+------------------------------------------------------------------+

void SaveLastSignal()

  {

   GlobalVariableSet(LastSignalGVName(), (double)TimeCurrent());

  }

 

 

//+------------------------------------------------------------------+

//| Official RiskGuard signal function - Config. 2                   |

//|                                                                  |

//| pendingPrice = 0.0  -> market order                              |

//| pendingPrice > 0.0  -> pending order                             |

//|                                                                  |

//| DO NOT MODIFY THIS FUNCTION.                                     |

//| This function must remain identical in all RiskGuard-compatible  |

//| indicators.                                                      |

//+------------------------------------------------------------------+

void SendRiskGuardConfig2Signal(double stopPrice,

                         double takePrice,

                         double pendingPrice = 0.0)

  {

   //--- Always delete old RiskGuard variables first

   GlobalVariableDel("RG_Strategy_Config2_Symbol_" + _Symbol);

   GlobalVariableDel("RG_Strategy_Config2_Stop_"   + _Symbol);

   GlobalVariableDel("RG_Strategy_Config2_Take_"   + _Symbol);

   GlobalVariableDel("RG_Strategy_Config2_Price_"  + _Symbol);

 

   //--- Normalize prices

   stopPrice = NormalizeDouble(stopPrice, _Digits);

   takePrice = NormalizeDouble(takePrice, _Digits);

 

   if(pendingPrice > 0.0)

      pendingPrice = NormalizeDouble(pendingPrice, _Digits);

 

   bool isPending = (pendingPrice > 0.0);

 

   //--- Create variables in the correct order

   GlobalVariableSet("RG_Strategy_Config2_Stop_" + _Symbol, stopPrice);

   GlobalVariableSet("RG_Strategy_Config2_Take_" + _Symbol, takePrice);

 

   if(isPending)

      GlobalVariableSet("RG_Strategy_Config2_Price_" + _Symbol, pendingPrice);

 

   //--- RG_Strategy_Symbol must always be created last

   GlobalVariableSet("RG_Strategy_Config2_Symbol_" + _Symbol, 1);

 

   //--- Log

   if(isPending)

     {

      Print("[", MQLInfoString(MQL_PROGRAM_NAME), "][INDICATOR][SIGNAL] Pending signal variables created",

            " | Symbol=", _Symbol,

            " | Stop=", DoubleToString(stopPrice, _Digits),

            " | Take=", DoubleToString(takePrice, _Digits),

            " | Price=", DoubleToString(pendingPrice, _Digits),

            " | Bid=", DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits),

            " | Ask=", DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits));

     }

   else

     {

      Print("[", MQLInfoString(MQL_PROGRAM_NAME), "][INDICATOR][SIGNAL] Market signal variables created",

            " | Symbol=", _Symbol,

            " | Stop=", DoubleToString(stopPrice, _Digits),

            " | Take=", DoubleToString(takePrice, _Digits),

            " | Bid=", DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits),

            " | Ask=", DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits));

     }

  }

 

 

//+------------------------------------------------------------------+

//| OnInit                                                           |

//+------------------------------------------------------------------+

int OnInit()

  {

   Print("[", MQLInfoString(MQL_PROGRAM_NAME), "][INDICATOR][OnInit] Indicator started",

         " | Symbol=", _Symbol);

 

   return(INIT_SUCCEEDED);

  }

 

 

//+------------------------------------------------------------------+

//| OnDeinit                                                         |

//+------------------------------------------------------------------+

//| Do not delete LastSignal during timeframe changes.               |

//| Otherwise the same signal could be generated again after reload. |

//+------------------------------------------------------------------+

void OnDeinit(const int reason)

  {

   if(reason == REASON_REMOVE)

     {

      GlobalVariableDel(LastSignalGVName());

 

      Print("[", MQLInfoString(MQL_PROGRAM_NAME),

            "][INDICATOR][OnDeinit] Indicator manually removed",

            " | Symbol=", _Symbol,

            " | LastSignalGV deleted");

     }

  }

 

 

//+------------------------------------------------------------------+

//| OnCalculate                                                      |

//+------------------------------------------------------------------+

int OnCalculate(const int rates_total,

                const int prev_calculated,

                const datetime &time[],

                const double &open[],

                const double &high[],

                const double &low[],

                const double &close[],

                const long &tick_volume[],

                const long &volume[],

                const int &spread[])

  {

   MqlDateTime tm;

   TimeToStruct(TimeCurrent(), tm);

 

   //--- This example uses broker/server time.

   if(tm.hour != SignalHour || tm.min != SignalMinute)

      return(rates_total);

 

   //--- Prevent duplicate signals in the same server minute.

   if(SignalAlreadySentThisMinute())

      return(rates_total);

 

   //--- The current Bid price is used as reference price.

   double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);

 

   if(price <= 0.0)

     {

      Print("[", MQLInfoString(MQL_PROGRAM_NAME), "][INDICATOR][ERROR] Invalid price",

            " | Symbol=", _Symbol);

      return(rates_total);

     }

 

   double stopPrice;

   double takePrice;

   double pendingPrice = 0.0;

 

   //--- Pending order example.

   if(PriceDistancePoints > 0)

     {

      pendingPrice = price + PriceDistancePoints * _Point;

 

      stopPrice = pendingPrice - StopDistancePoints * _Point;

      takePrice = pendingPrice + TakeDistancePoints * _Point;

 

      SendRiskGuardConfig2Signal(stopPrice, takePrice, pendingPrice);

     }

 

   //--- Market order example.

   else

     {

      stopPrice = price - StopDistancePoints * _Point;

      takePrice = price + TakeDistancePoints * _Point;

 

      SendRiskGuardConfig2Signal(stopPrice, takePrice);

     }

 

   //--- Save only after the RiskGuard variables have been created.

   SaveLastSignal();

 

   return(rates_total);

  }

//+------------------------------------------------------------------+

Example 2 — RiskGuard_ORB.mq5
 

An advanced example indicator based on an Opening Range Breakout (ORB) strategy.

The indicator calculates a user-defined time range, draws the range box directly on the chart, and generates a signal when a closed candle breaks above or below the range.

This example demonstrates a more realistic structure, closely resembling a complete automated trading strategy.

Main features:

  • Automatic calculation of the ORB range

  • Draws the range box directly on the chart

  • Detects BUY and SELL breakouts based on candle close

  • Supports market and pending orders

  • Calculates Stop Loss and Take Profit using the selected Risk ratio

  • Prevents duplicate signals

  • Prevents retroactive signals

  • Maintains consistent behavior when the chart timeframe changes

  • Detects Strategy Mode orders using Magic Number 30032999

This example is recommended for developers who want to build more advanced strategies compatible with RiskGuard.

//+------------------------------------------------------------------+

//|                                      RiskGuard_ORB_Example.mq5   |

//|                                                                  |

//| Example indicator for RiskGuard Strategy Mode - Config. 2        |

//|                                                                  |

//| This indicator does NOT open trades directly.                    |

//| It only sends market or pending signals to RiskGuard using the   |

//| official RiskGuard Config. 2 protocol.                           |

//|                                                                  |

//| RiskGuard manages the order, lot size, risk, Stop Loss,           |

//| Take Profit, Break Even, partial profits, trailing stop,          |

//| daily loss limits, Quantum and all other trading operations.      |

//+------------------------------------------------------------------+

#property strict

#property indicator_chart_window

#property indicator_plots 0

 

 

//+------------------------------------------------------------------+

//| Strategy inputs                                                  |

//+------------------------------------------------------------------+

input int RangeStartHour   = 6;   // ORB range start hour

input int RangeStartMinute = 0;   // ORB range start minute

 

input int RangeEndHour     = 11;  // ORB range end hour

input int RangeEndMinute   = 0;   // ORB range end minute

 

input ENUM_TIMEFRAMES BreakoutTimeframe = PERIOD_M1; // Breakout detection timeframe

 

input double EntryPercent = 0.0;  // 0 = market, 50 = pending at 50% of the box

input double RiskReward   = 2.0;  // Take Profit expressed as Risk:Reward ratio

 

input color BoxColor = clrDodgerBlue; // ORB box color

 

 

//+------------------------------------------------------------------+

//| Internal variables                                               |

//+------------------------------------------------------------------+

datetime IndicatorLoadTime = 0;

datetime LastBreakoutBarTime = 0;

 

bool ActivityDetectedAfterSignal = false;

bool ShowGraphics = true;

 

 

//+------------------------------------------------------------------+

//| Official RiskGuard signal function - Config. 2                   |

//|                                                                  |

//| pendingPrice = 0.0  -> market order                              |

//| pendingPrice > 0.0  -> pending order                             |

//|                                                                  |

//| DO NOT MODIFY THIS FUNCTION.                                     |

//| This function must remain identical in all RiskGuard-compatible  |

//| indicators.                                                      |

//+------------------------------------------------------------------+

void SendRiskGuardConfig2Signal(double stopPrice,

                         double takePrice,

                         double pendingPrice = 0.0)

  {

   //--- Always delete old RiskGuard variables first

   GlobalVariableDel("RG_Strategy_Config2_Symbol_" + _Symbol);

   GlobalVariableDel("RG_Strategy_Config2_Stop_"   + _Symbol);

   GlobalVariableDel("RG_Strategy_Config2_Take_"   + _Symbol);

   GlobalVariableDel("RG_Strategy_Config2_Price_"  + _Symbol);

 

   //--- Normalize prices

   stopPrice = NormalizeDouble(stopPrice, _Digits);

   takePrice = NormalizeDouble(takePrice, _Digits);

 

   if(pendingPrice > 0.0)

      pendingPrice = NormalizeDouble(pendingPrice, _Digits);

 

   bool isPending = (pendingPrice > 0.0);

 

   //--- Create variables in the correct order

   GlobalVariableSet("RG_Strategy_Config2_Stop_" + _Symbol, stopPrice);

   GlobalVariableSet("RG_Strategy_Config2_Take_" + _Symbol, takePrice);

 

   if(isPending)

      GlobalVariableSet("RG_Strategy_Config2_Price_" + _Symbol, pendingPrice);

 

   //--- RG_Strategy_Symbol must always be created last

   GlobalVariableSet("RG_Strategy_Config2_Symbol_" + _Symbol, 1);

 

   //--- Log

   if(isPending)

     {

      Print("[", MQLInfoString(MQL_PROGRAM_NAME), "][INDICATOR][SIGNAL] Pending signal variables created",

            " | Symbol=", _Symbol,

            " | Stop=", DoubleToString(stopPrice, _Digits),

            " | Take=", DoubleToString(takePrice, _Digits),

            " | Price=", DoubleToString(pendingPrice, _Digits),

            " | Bid=", DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits),

            " | Ask=", DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits));

     }

   else

     {

      Print("[", MQLInfoString(MQL_PROGRAM_NAME), "][INDICATOR][SIGNAL] Market signal variables created",

            " | Symbol=", _Symbol,

            " | Stop=", DoubleToString(stopPrice, _Digits),

            " | Take=", DoubleToString(takePrice, _Digits),

            " | Bid=", DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits),

            " | Ask=", DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits));

     }

  }

 

 

//+------------------------------------------------------------------+

//| Returns a numeric key for the trading day                        |

//+------------------------------------------------------------------+

int DayKey(datetime timeValue)

  {

   MqlDateTime dateTime;

   TimeToStruct(timeValue, dateTime);

 

   return(dateTime.year * 10000 +

          dateTime.mon  * 100 +

          dateTime.day);

  }

 

 

//+------------------------------------------------------------------+

//| Builds today's broker/server time                                |

//+------------------------------------------------------------------+

datetime TodayTime(int hour, int minute)

  {

   MqlDateTime dateTime;

   TimeToStruct(TimeCurrent(), dateTime);

 

   dateTime.hour = hour;

   dateTime.min  = minute;

   dateTime.sec  = 0;

 

   return(StructToTime(dateTime));

  }

 

 

//+------------------------------------------------------------------+

//| Internal Global Variable used for duplicate protection           |

//|                                                                  |

//| This is NOT a RiskGuard trade signal.                            |

//|                                                                  |

//| It remembers that the ORB signal for the current day has already |

//| been sent. It must remain available during timeframe changes,    |

//| input changes, refreshes and indicator reinitializations.         |

//+------------------------------------------------------------------+

string LastSignalGVName()

  {

   return("RiskGuard_ORB_LastSignal_" +

          MQLInfoString(MQL_PROGRAM_NAME) +

          "_" +

          _Symbol);

  }

 

 

//+------------------------------------------------------------------+

//| Checks if today's ORB signal has already been sent               |

//+------------------------------------------------------------------+

bool AlreadySentToday()

  {

   string globalVariableName = LastSignalGVName();

 

   if(!GlobalVariableCheck(globalVariableName))

      return(false);

 

   int savedDay = (int)GlobalVariableGet(globalVariableName);

 

   return(savedDay == DayKey(TimeCurrent()));

  }

 

 

//+------------------------------------------------------------------+

//| Saves today's ORB signal status                                  |

//+------------------------------------------------------------------+

void SaveSignalToday()

  {

   GlobalVariableSet(LastSignalGVName(),

                     (double)DayKey(TimeCurrent()));

  }

 

 

//+------------------------------------------------------------------+

//| Returns today's ORB box name                                     |

//+------------------------------------------------------------------+

string BoxName()

  {

   return("RiskGuard_ORB_Box_" +

          MQLInfoString(MQL_PROGRAM_NAME) +

          "_" +

          _Symbol +

          "_" +

          IntegerToString(DayKey(TimeCurrent())));

  }

 

 

//+------------------------------------------------------------------+

//| Deletes today's ORB box                                          |

//+------------------------------------------------------------------+

void DeleteBox()

  {

   if(!ShowGraphics)

      return;

 

   ObjectDelete(0, BoxName());

  }

 

 

//+------------------------------------------------------------------+

//| Deletes ORB boxes belonging to previous days                     |

//+------------------------------------------------------------------+

void DeleteOldBoxes()

  {

   if(!ShowGraphics)

      return;

 

   string prefix =

      "RiskGuard_ORB_Box_" +

      MQLInfoString(MQL_PROGRAM_NAME) +

      "_" +

      _Symbol +

      "_";

 

   string today = IntegerToString(DayKey(TimeCurrent()));

 

   int totalObjects = ObjectsTotal(0);

 

   for(int i = totalObjects - 1; i >= 0; i--)

     {

      string objectName = ObjectName(0, i);

 

      if(StringFind(objectName, prefix) != 0)

         continue;

 

      if(StringFind(objectName, today) >= 0)

         continue;

 

      ObjectDelete(0, objectName);

     }

  }

 

 

//+------------------------------------------------------------------+

//| Checks RiskGuard Strategy positions and pending orders           |

//|                                                                  |

//| RiskGuard Config. 2 Strategy orders use Magic Number 30032999.   |

//+------------------------------------------------------------------+

bool HasRiskGuardStrategyPositionOrOrder()

  {

   const long strategyMagic = 30032999;

 

   for(int i = PositionsTotal() - 1; i >= 0; i--)

     {

      string positionSymbol = PositionGetSymbol(i);

 

      if(positionSymbol != _Symbol)

         continue;

 

      long positionMagic = PositionGetInteger(POSITION_MAGIC);

 

      if(positionMagic == strategyMagic)

         return(true);

     }

 

   for(int i = OrdersTotal() - 1; i >= 0; i--)

     {

      ulong ticket = OrderGetTicket(i);

 

      if(ticket == 0)

         continue;

 

      string orderSymbol = OrderGetString(ORDER_SYMBOL);

 

      if(orderSymbol != _Symbol)

         continue;

 

      long orderMagic = OrderGetInteger(ORDER_MAGIC);

 

      if(orderMagic == strategyMagic)

         return(true);

     }

 

   return(false);

  }

 

 

//+------------------------------------------------------------------+

//| Calculates today's ORB range                                     |

//+------------------------------------------------------------------+

bool GetOrbRange(double &rangeHigh,

                 double &rangeLow)

  {

   datetime startTime =

      TodayTime(RangeStartHour, RangeStartMinute);

 

   datetime endTime =

      TodayTime(RangeEndHour, RangeEndMinute);

 

   if(endTime <= startTime)

     {

      Print("[",

            MQLInfoString(MQL_PROGRAM_NAME),

            "][INDICATOR][ERROR] Invalid ORB time range",

            " | Start=",

            TimeToString(startTime, TIME_DATE | TIME_MINUTES),

            " | End=",

            TimeToString(endTime, TIME_DATE | TIME_MINUTES));

 

      return(false);

     }

 

   MqlRates rangeRates[];

   ArraySetAsSeries(rangeRates, false);

 

   int copiedBars =

      CopyRates(_Symbol,

                BreakoutTimeframe,

                startTime,

                endTime,

                rangeRates);

 

   if(copiedBars <= 0)

      return(false);

 

   rangeHigh = rangeRates[0].high;

   rangeLow  = rangeRates[0].low;

 

   for(int i = 1; i < copiedBars; i++)

     {

      if(rangeRates[i].high > rangeHigh)

         rangeHigh = rangeRates[i].high;

 

      if(rangeRates[i].low < rangeLow)

         rangeLow = rangeRates[i].low;

     }

 

   rangeHigh = NormalizeDouble(rangeHigh, _Digits);

   rangeLow  = NormalizeDouble(rangeLow, _Digits);

 

   if(rangeHigh <= rangeLow)

      return(false);

 

   return(true);

  }

 

 

//+------------------------------------------------------------------+

//| Creates or updates today's ORB box                               |

//+------------------------------------------------------------------+

void DrawBox(double rangeHigh,

             double rangeLow)

  {

   if(!ShowGraphics)

      return;

 

   datetime startTime =

      TodayTime(RangeStartHour, RangeStartMinute);

 

   datetime endTime =

      TodayTime(RangeEndHour, RangeEndMinute);

 

   string objectName = BoxName();

 

   if(ObjectFind(0, objectName) < 0)

     {

      if(!ObjectCreate(0,

                       objectName,

                       OBJ_RECTANGLE,

                       0,

                       startTime,

                       rangeHigh,

                       endTime,

                       rangeLow))

        {

         Print("[",

               MQLInfoString(MQL_PROGRAM_NAME),

               "][INDICATOR][ERROR] Unable to create ORB box",

               " | Error=",

               GetLastError());

 

         return;

        }

 

      ObjectSetInteger(0, objectName, OBJPROP_COLOR, BoxColor);

      ObjectSetInteger(0, objectName, OBJPROP_BACK, true);

      ObjectSetInteger(0, objectName, OBJPROP_FILL, false);

      ObjectSetInteger(0, objectName, OBJPROP_WIDTH, 1);

      ObjectSetInteger(0, objectName, OBJPROP_SELECTABLE, false);

      ObjectSetInteger(0, objectName, OBJPROP_HIDDEN, true);

     }

   else

     {

      ObjectMove(0,

                 objectName,

                 0,

                 startTime,

                 rangeHigh);

 

      ObjectMove(0,

                 objectName,

                 1,

                 endTime,

                 rangeLow);

     }

  }

 

 

//+------------------------------------------------------------------+

//| Validates the strategy inputs                                    |

//+------------------------------------------------------------------+

bool ValidateInputs()

  {

   if(RangeStartHour < 0 || RangeStartHour > 23)

     {

      Print("[",

            MQLInfoString(MQL_PROGRAM_NAME),

            "][INDICATOR][ERROR] Invalid RangeStartHour");

 

      return(false);

     }

 

   if(RangeEndHour < 0 || RangeEndHour > 23)

     {

      Print("[",

            MQLInfoString(MQL_PROGRAM_NAME),

            "][INDICATOR][ERROR] Invalid RangeEndHour");

 

      return(false);

     }

 

   if(RangeStartMinute < 0 || RangeStartMinute > 59)

     {

      Print("[",

            MQLInfoString(MQL_PROGRAM_NAME),

            "][INDICATOR][ERROR] Invalid RangeStartMinute");

 

      return(false);

     }

 

   if(RangeEndMinute < 0 || RangeEndMinute > 59)

     {

      Print("[",

            MQLInfoString(MQL_PROGRAM_NAME),

            "][INDICATOR][ERROR] Invalid RangeEndMinute");

 

      return(false);

     }

 

   if(RiskReward <= 0.0)

     {

      Print("[",

            MQLInfoString(MQL_PROGRAM_NAME),

            "][INDICATOR][ERROR] RiskReward must be greater than zero");

 

      return(false);

     }

 

   datetime startTime =

      TodayTime(RangeStartHour, RangeStartMinute);

 

   datetime endTime =

      TodayTime(RangeEndHour, RangeEndMinute);

 

   if(endTime <= startTime)

     {

      Print("[",

            MQLInfoString(MQL_PROGRAM_NAME),

            "][INDICATOR][ERROR] Range end time must be later than range start time");

 

      return(false);

     }

 

   return(true);

  }

 

 

//+------------------------------------------------------------------+

//| OnInit                                                           |

//+------------------------------------------------------------------+

int OnInit()

  {

   IndicatorLoadTime = TimeCurrent();

   LastBreakoutBarTime = 0;

   ActivityDetectedAfterSignal = false;

 

   ShowGraphics = true;

 

   if(MQLInfoInteger(MQL_TESTER) &&

      !MQLInfoInteger(MQL_VISUAL_MODE))

     {

      ShowGraphics = false;

     }

 

   if(!ValidateInputs())

      return(INIT_PARAMETERS_INCORRECT);

 

   Print("[",

         MQLInfoString(MQL_PROGRAM_NAME),

         "][INDICATOR][OnInit] ORB indicator started",

         " | Symbol=",

         _Symbol,

         " | BreakoutTimeframe=",

         EnumToString(BreakoutTimeframe),

         " | ShowGraphics=",

         ShowGraphics ? "true" : "false",

         " | LoadTime=",

         TimeToString(IndicatorLoadTime,

                      TIME_DATE | TIME_SECONDS));

 

   return(INIT_SUCCEEDED);

  }

 

 

//+------------------------------------------------------------------+

//| OnDeinit                                                         |

//|                                                                  |

//| The duplicate-protection Global Variable is not deleted during   |

//| timeframe changes, input changes or other reinitializations.      |

//|                                                                  |

//| It is deleted only when the indicator is manually removed.       |

//+------------------------------------------------------------------+

void OnDeinit(const int reason)

  {

   if(reason == REASON_REMOVE)

     {

      GlobalVariableDel(LastSignalGVName());

 

      if(ShowGraphics)

         DeleteBox();

     }

 

   Print("[",

         MQLInfoString(MQL_PROGRAM_NAME),

         "][INDICATOR][OnDeinit] ORB indicator stopped",

         " | Symbol=",

         _Symbol,

         " | Reason=",

         reason);

  }

 

 

//+------------------------------------------------------------------+

//| OnCalculate                                                      |

//|                                                                  |

//| Main workflow:                                                   |

//|                                                                  |

//| 1. Wait until the ORB range is completed.                        |

//| 2. Calculate the ORB high and low.                               |

//| 3. Draw the box only when graphics are enabled.                  |

//| 4. Detect a breakout using a closed candle.                      |

//| 5. Calculate Stop Loss and Take Profit.                          |

//| 6. Send the signal to RiskGuard.                                 |

//| 7. Save the daily duplicate-protection status.                   |

//+------------------------------------------------------------------+

int OnCalculate(const int rates_total,

                const int prev_calculated,

                const datetime &time[],

                const double &open[],

                const double &high[],

                const double &low[],

                const double &close[],

                const long &tick_volume[],

                const long &volume[],

                const int &spread[])

  {

   if(ShowGraphics)

      DeleteOldBoxes();

 

   datetime now = TimeCurrent();

 

   datetime endTime =

      TodayTime(RangeEndHour, RangeEndMinute);

 

   //--- Wait until the ORB range is completed.

   if(now < endTime)

      return(rates_total);

 

   double rangeHigh = 0.0;

   double rangeLow  = 0.0;

 

   if(!GetOrbRange(rangeHigh, rangeLow))

      return(rates_total);

 

   bool signalAlreadySent = AlreadySentToday();

 

   //--- Draw the box only in live use or visual backtests.

   if(ShowGraphics && !signalAlreadySent)

      DrawBox(rangeHigh, rangeLow);

 

   //--- Manage the box after a signal has already been sent.

   if(signalAlreadySent)

     {

      bool hasStrategyActivity =

         HasRiskGuardStrategyPositionOrOrder();

 

      if(hasStrategyActivity)

         ActivityDetectedAfterSignal = true;

 

      if(ShowGraphics &&

         ActivityDetectedAfterSignal &&

         !hasStrategyActivity)

        {

         DeleteBox();

        }

 

      return(rates_total);

     }

 

   //--- Prevent retroactive signals.

   //--- If the indicator is loaded today after the range has ended,

   //--- no breakout that occurred before loading may be recovered.

   if(DayKey(IndicatorLoadTime) == DayKey(now) &&

      IndicatorLoadTime > endTime)

     {

      return(rates_total);

     }

 

   MqlRates breakoutRates[];

   ArraySetAsSeries(breakoutRates, true);

 

   int copiedBars =

      CopyRates(_Symbol,

                BreakoutTimeframe,

                0,

                3,

                breakoutRates);

 

   if(copiedBars < 3)

      return(rates_total);

 

   //--- Use only the last closed candle.

   datetime closedBarTime = breakoutRates[1].time;

 

   //--- Process each closed candle only once.

   if(closedBarTime == LastBreakoutBarTime)

      return(rates_total);

 

   LastBreakoutBarTime = closedBarTime;

 

   //--- Ignore candles closed before completion of the ORB range.

   if(closedBarTime < endTime)

      return(rates_total);

 

   double barClose = breakoutRates[1].close;

   double boxSize  = rangeHigh - rangeLow;

 

   if(boxSize <= 0.0)

      return(rates_total);

 

   double normalizedEntryPercent = EntryPercent;

 

   if(normalizedEntryPercent < 0.0)

      normalizedEntryPercent = 0.0;

 

   if(normalizedEntryPercent > 99.0)

      normalizedEntryPercent = 99.0;

 

   double ask =

      SymbolInfoDouble(_Symbol, SYMBOL_ASK);

 

   double bid =

      SymbolInfoDouble(_Symbol, SYMBOL_BID);

 

   if(ask <= 0.0 || bid <= 0.0)

      return(rates_total);

 

 

   //+---------------------------------------------------------------+

   //| BUY breakout                                                  |

   //+---------------------------------------------------------------+

   if(barClose > rangeHigh)

     {

      double stopPrice    = 0.0;

      double takePrice    = 0.0;

      double pendingPrice = 0.0;

 

      //--- Market BUY signal.

      if(normalizedEntryPercent <= 0.0)

        {

         stopPrice = rangeLow;

 

         double riskDistance = ask - stopPrice;

 

         if(riskDistance <= 0.0)

            return(rates_total);

 

         takePrice =

            ask + riskDistance * RiskReward;

 

         SendRiskGuardConfig2Signal(stopPrice,

                                    takePrice);

        }

 

      //--- Pending BUY signal inside the ORB box.

      else

        {

         pendingPrice =

            rangeHigh -

            boxSize *

            normalizedEntryPercent /

            100.0;

 

         stopPrice = rangeLow;

 

         double riskDistance =

            pendingPrice - stopPrice;

 

         if(riskDistance <= 0.0)

            return(rates_total);

 

         takePrice =

            pendingPrice +

            riskDistance *

            RiskReward;

 

         SendRiskGuardConfig2Signal(stopPrice,

                                    takePrice,

                                    pendingPrice);

        }

 

      //--- Save only after the RiskGuard signal variables exist.

      SaveSignalToday();

 

      return(rates_total);

     }

 

 

   //+---------------------------------------------------------------+

   //| SELL breakout                                                 |

   //+---------------------------------------------------------------+

   if(barClose < rangeLow)

     {

      double stopPrice    = 0.0;

      double takePrice    = 0.0;

      double pendingPrice = 0.0;

 

      //--- Market SELL signal.

      if(normalizedEntryPercent <= 0.0)

        {

         stopPrice = rangeHigh;

 

         double riskDistance =

            stopPrice - bid;

 

         if(riskDistance <= 0.0)

            return(rates_total);

 

         takePrice =

            bid -

            riskDistance *

            RiskReward;

 

         SendRiskGuardConfig2Signal(stopPrice,

                                    takePrice);

        }

 

      //--- Pending SELL signal inside the ORB box.

      else

        {

         pendingPrice =

            rangeLow +

            boxSize *

            normalizedEntryPercent /

            100.0;

 

         stopPrice = rangeHigh;

 

         double riskDistance =

            stopPrice - pendingPrice;

 

         if(riskDistance <= 0.0)

            return(rates_total);

 

         takePrice =

            pendingPrice -

            riskDistance *

            RiskReward;

 

         SendRiskGuardConfig2Signal(stopPrice,

                                    takePrice,

                                    pendingPrice);

        }

 

      //--- Save only after the RiskGuard signal variables exist.

      SaveSignalToday();

 

      return(rates_total);

     }

 

   return(rates_total);

  }

//+------------------------------------------------------------------+

bottom of page