A Estratégia At Random Full é uma conversão fiel do consultor especialista MetaTrader 5 "At random Full". Ele mantém o
ideia original de abrir negociações com base em um gerador aleatório enquanto expõe as mesmas opções de gerenciamento de dinheiro: direção
filtros, espaçamento de grade, janelas de tempo opcionais e um botão liga/desliga para cálculo da média. A porta StockSharp usa o API de alto nível,
portanto, todo o ciclo de decisão é conduzido por assinaturas de velas e ajudantes StartProtection padrão para ordens de proteção.
Lógica de negociação
Em cada vela finalizada a estratégia verifica se a negociação é permitida (filtro de sessão, estado do portfólio e opcional
sinalizador "apenas uma posição").
Um gerador pseudo-aleatório decide entre uma entrada longa ou curta. O parâmetro ReverseSignals pode inverter o resultado para
emular o modo reverso MQL.
Filtros de direção (TradeMode) bloqueiam sinais indesejados. O código também impõe a regra original EA de uma única negociação por
barra em cada direção, lembrando o tempo de abertura da vela do último sinal.
As opções de gerenciamento de grade refletem o comportamento MetaTrader:
MaxPositions limita o número médio de entradas por lado.
MinStepPoints requer uma distância mínima (convertida em preço usando a etapa de preço de segurança) entre entradas consecutivas.
CloseOpposite força o fechamento da exposição oposta existente antes que uma nova negociação seja enviada.
As ordens de mercado são emitidas através de BuyMarket / SellMarket com um volume normalizado definido por OrderVolume.
Gestão de Posição e Risco
StartProtection anexa ordens de stop-loss e take-profit que correspondem às entradas de MetaTrader. Se TrailingStopPoints for
maior que zero, o modo de rastreamento StockSharp integrado está ativado. Os parâmetros TrailingActivatePoints e
TrailingStepPoints são convertidos em distâncias de preços e registrados para transparência, mas o rastreamento real é tratado pelo
plataforma.
Todos os cálculos de volume respeitam os metadados de troca (mínimo, máximo e passo) exatamente como as rotinas auxiliares MQL.
O controle de tempo emula o bloco InpTimeControl do script. Quando habilitado, as negociações são permitidas apenas dentro do configurado
janela [SessionStart, SessionEnd]; sessões noturnas são suportadas.
Parâmetros
Parâmetro
Descrição
Padrão
CandleType
Série de velas usada para agendar o ciclo de decisão.
15 minute timeframe
OrderVolume
Volume base de ordens de mercado em lotes.
0.1
MaxPositions
Número máximo de entradas médias por direção (0 = ilimitado).
5
MinStepPoints
Distância mínima entre entradas expressa em MetaTrader pontos.
150
StopLossPoints
Distância de stop-loss em pontos.
150
TakeProfitPoints
Distância de lucro em pontos.
460
TrailingActivatePoints
Limite de lucro (em pontos) registrado para fins informativos quando o rastreamento está ativado.
70
TrailingStopPoints
Distância de parada final passada para StartProtection.
250
TrailingStepPoints
Passo entre os ajustes finais, registrados junto com a distância de ativação.
50
OnlyOnePosition
Bloqueia novas negociações até que a posição líquida atual seja fechada.
false
CloseOpposite
Fecha a exposição oposta antes de abrir uma negociação.
false
ReverseSignals
Inverte a decisão aleatória para que as compras se tornem vendas e vice-versa.
false
UseTimeControl
Habilita o filtro de horário da sessão de negociação.
false
SessionStart
Hora de início da sessão (inclusive) quando UseTimeControl for true.
10:01
SessionEnd
Hora de término da sessão (inclusive) quando UseTimeControl for true.
Semente determinística opcional para o gerador pseudo-aleatório (0 = contagem de ticks do ambiente).
0
Notas de implementação
Todos os comentários são escritos em inglês e o código usa recuo de tabulação, correspondendo às diretrizes do repositório.
O processamento de velas depende de SubscribeCandles().Bind(...), garantindo que a lógica seja executada uma vez por barra concluída, como em EA.
A estratégia acompanha os últimos preços de compra e venda para impor a restrição de espaçamento mínimo, mesmo durante o cálculo da média.
As instruções de registro refletem os diagnósticos detalhados impressos pelo script original: cada entrada anuncia a direção escolhida,
preço de entrada, volume e configuração final na inicialização.
Dicas de uso
Como o sinal de negociação é aleatório, a estratégia é mais adequada para testar infraestruturas ou demonstrar controlos de risco.
Ajuste OrderVolume, StopLossPoints e TakeProfitPoints para alinhar com o tamanho do tick e a volatilidade do instrumento que você
planeja negociar.
Ative UseTimeControl se o EA deve operar apenas durante uma sessão específica (por exemplo, a sessão de Londres ou Nova York).
Use RandomSeed durante as execuções de otimização para obter sequências reproduzíveis de decisões aleatórias.
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()