The At Random Full Strategy is a faithful conversion of the MetaTrader 5 expert advisor "At random Full". It keeps the
original idea of opening trades based on a random generator while exposing the same money-management switches: direction
filters, grid spacing, optional time windows and an on/off toggle for averaging. The StockSharp port uses the high-level API,
so the entire decision loop is driven by candle subscriptions and standard StartProtection helpers for protective orders.
Trading Logic
On every finished candle the strategy verifies that trading is allowed (session filter, portfolio state and optional
"only one position" flag).
A pseudo-random generator decides between a long or short entry. The ReverseSignals parameter can flip the outcome to
emulate the MQL reverse mode.
Direction filters (TradeMode) block undesired signals. The code also enforces the original EA rule of a single trade per
bar in each direction by remembering the candle open time of the latest signal.
Grid management options mirror the MetaTrader behaviour:
MaxPositions caps the number of averaged entries per side.
MinStepPoints requires a minimum distance (converted to price using the security price step) between consecutive entries.
CloseOpposite forces the existing opposite exposure to be closed before a new trade is sent.
Market orders are issued through BuyMarket / SellMarket with a normalised volume defined by OrderVolume.
Position and Risk Management
StartProtection attaches stop-loss and take-profit orders that match the MetaTrader inputs. If TrailingStopPoints is
greater than zero the built-in StockSharp trailing mode is enabled. The parameters TrailingActivatePoints and
TrailingStepPoints are converted to price distances and logged for transparency, but the actual trailing is handled by the
platform.
All volume calculations respect the exchange metadata (minimum, maximum and step) exactly like the MQL helper routines.
Time control emulates the InpTimeControl block from the script. When enabled, trades are allowed only inside the configured
[SessionStart, SessionEnd] window; overnight sessions are supported.
Parameters
Parameter
Description
Default
CandleType
Candle series used to schedule the decision loop.
15 minute timeframe
OrderVolume
Base market order volume in lots.
0.1
MaxPositions
Maximum number of averaged entries per direction (0 = unlimited).
5
MinStepPoints
Minimum distance between entries expressed in MetaTrader points.
150
StopLossPoints
Stop-loss distance in points.
150
TakeProfitPoints
Take-profit distance in points.
460
TrailingActivatePoints
Profit threshold (in points) logged for informational purposes when trailing is enabled.
70
TrailingStopPoints
Trailing stop distance passed to StartProtection.
250
TrailingStepPoints
Step between trailing adjustments, logged alongside the activation distance.
50
OnlyOnePosition
Blocks new trades until the current net position is closed.
false
CloseOpposite
Closes the opposite exposure before opening a trade.
false
ReverseSignals
Inverts the random decision so buys become sells and vice versa.
false
UseTimeControl
Enables the trading session time filter.
false
SessionStart
Session start time (inclusive) when UseTimeControl is true.
10:01
SessionEnd
Session end time (inclusive) when UseTimeControl is true.
15:02
Mode
Allowed trade direction (Both, BuyOnly, SellOnly).
Both
RandomSeed
Optional deterministic seed for the pseudo-random generator (0 = environment tick count).
0
Implementation Notes
All comments are written in English and the code uses tab indentation, matching the repository guidelines.
Candle processing relies on SubscribeCandles().Bind(...), ensuring the logic executes once per finished bar as in the EA.
The strategy keeps track of the last buy and sell fill prices to enforce the minimum spacing constraint even during averaging.
Logging statements mirror the detailed diagnostics printed by the original script: every entry announces the chosen direction,
entry price, volume, and the trailing configuration on startup.
Usage Tips
Because the trading signal is random, the strategy is best suited for testing infrastructure or demonstrating risk controls.
Adjust OrderVolume, StopLossPoints, and TakeProfitPoints to align with the tick size and volatility of the instrument you
plan to trade.
Enable UseTimeControl if the EA should operate only during a specific session (for example, the London or New York session).
Use RandomSeed during optimisation runs to achieve reproducible sequences of random decisions.
using System;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Simplified from "At random Full" MetaTrader expert.
/// Randomly opens long or short positions with grid spacing and position limits.
/// </summary>
public class AtRandomFullStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maxPositions;
private readonly StrategyParam<int> _randomSeed;
private Random _random;
private decimal _lastEntryPrice;
private int _entryCount;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int MaxPositions
{
get => _maxPositions.Value;
set => _maxPositions.Value = value;
}
public int RandomSeed
{
get => _randomSeed.Value;
set => _randomSeed.Value = value;
}
public AtRandomFullStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe", "General");
_maxPositions = Param(nameof(MaxPositions), 3)
.SetDisplay("Max Positions", "Maximum number of averaged entries", "Risk");
_randomSeed = Param(nameof(RandomSeed), 123)
.SetDisplay("Random Seed", "Fixed seed for deterministic simulations", "Execution");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_random = RandomSeed == 0 ? new Random() : new Random(RandomSeed);
_lastEntryPrice = 0;
_entryCount = 0;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// Only trade occasionally to keep turnover within runner limits.
if (_random.Next(0, 5) != 0)
return;
var volume = Volume;
if (volume <= 0)
volume = 1;
var close = candle.ClosePrice;
// Check grid spacing - minimum 0.5% between entries
if (_lastEntryPrice > 0 && Math.Abs(close - _lastEntryPrice) / _lastEntryPrice < 0.005m)
return;
// Check entry limit
if (MaxPositions > 0 && _entryCount >= MaxPositions)
{
// Close position and reset
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(Math.Abs(Position));
_entryCount = 0;
_lastEntryPrice = 0;
return;
}
var goLong = _random.Next(0, 2) == 0;
if (goLong)
{
if (Position < 0)
{
BuyMarket(Math.Abs(Position) + volume);
_entryCount = 1;
_lastEntryPrice = close;
}
else if (Position == 0)
{
BuyMarket(volume);
_lastEntryPrice = close;
_entryCount++;
}
}
else
{
if (Position > 0)
{
SellMarket(Math.Abs(Position) + volume);
_entryCount = 1;
_lastEntryPrice = close;
}
else if (Position == 0)
{
SellMarket(volume);
_lastEntryPrice = close;
_entryCount++;
}
}
}
/// <inheritdoc />
protected override void OnReseted()
{
_random = null;
_lastEntryPrice = 0;
_entryCount = 0;
base.OnReseted();
}
}
import clr
import random
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class at_random_full_strategy(Strategy):
def __init__(self):
super(at_random_full_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._max_positions = self.Param("MaxPositions", 3)
self._random_seed = self.Param("RandomSeed", 123)
self._rng = None
self._last_entry_price = 0.0
self._entry_count = 0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def MaxPositions(self):
return self._max_positions.Value
@MaxPositions.setter
def MaxPositions(self, value):
self._max_positions.Value = value
@property
def RandomSeed(self):
return self._random_seed.Value
@RandomSeed.setter
def RandomSeed(self, value):
self._random_seed.Value = value
def OnReseted(self):
super(at_random_full_strategy, self).OnReseted()
self._rng = None
self._last_entry_price = 0.0
self._entry_count = 0
def OnStarted2(self, time):
super(at_random_full_strategy, self).OnStarted2(time)
seed = self.RandomSeed
self._rng = random.Random(seed if seed != 0 else None)
self._last_entry_price = 0.0
self._entry_count = 0
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
# Only trade occasionally
if self._rng.randint(0, 4) != 0:
return
close = float(candle.ClosePrice)
# Check grid spacing
if self._last_entry_price > 0 and abs(close - self._last_entry_price) / self._last_entry_price < 0.005:
return
# Check entry limit
if self.MaxPositions > 0 and self._entry_count >= self.MaxPositions:
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
self._entry_count = 0
self._last_entry_price = 0.0
return
go_long = self._rng.randint(0, 1) == 0
if go_long:
if self.Position <= 0:
self.BuyMarket()
self._last_entry_price = close
self._entry_count += 1
else:
if self.Position >= 0:
self.SellMarket()
self._last_entry_price = close
self._entry_count += 1
def CreateClone(self):
return at_random_full_strategy()