Estratégia de Aleatoriedade RRS
Visão geral
A Estratégia de Aleatoriedade RRS é uma versão StockSharp da “RRS Aleatoriedade na Natureza EA” para MetaTrader 4.
Ele emula o consultor especialista original, gerando entradas aleatórias de compra ou venda no mercado, aplica níveis de stop-loss e take-profit, opcionalmente rastreia negociações lucrativas e executa liquidação baseada em risco quando as perdas flutuantes excedem o limite configurado.
Como StockSharp usa posições líquidas por título, a exposição simultânea longa e curta não é suportada. O modo "DoubleSide", portanto, alterna a direção de entrada em cada oportunidade, em vez de manter duas negociações cobertas como em MetaTrader.
Lógica de negociação
- Em cada vela finalizada, a estratégia avalia o último preço de mercado obtido em negociações ou cotações de Nível 1.
- Se houver uma posição aberta, ele impõe regras de stop-loss, take-profit e trailing-stop e realiza uma verificação de risco do portfólio.
- Quando estável, valida as restrições de spread e volume antes de abrir uma nova negociação:
- O modo DoubleSide alterna entre entradas longas e curtas.
- O modo OneSide segue a regra original EA: um número inteiro aleatório em
[0,5] abre posições compradas para valores 1 ou 4 e posições vendidas para 0 ou 3.
- Os volumes de negociação são desenhados uniformemente entre o mínimo e o máximo configurados e alinhados ao passo de volume do instrumento.
Parâmetros
| Grupo |
Nome |
Descrição |
| Geral |
Mode |
Modo de negociação: entradas alternativas (DoubleSide) ou entradas aleatórias fechadas (OneSide). |
| Configurações de lote |
MinVolume / MaxVolume |
Faixa de volume para negociações geradas aleatoriamente. |
| Proteção |
TakeProfitPoints |
Distância de lucro em etapas de preço. |
| Proteção |
StopLossPoints |
Distância de stop-loss em etapas de preço. |
| Proteção |
TrailingStartPoints |
Distância de lucro que permite o gerenciamento de trailing stop. |
| Proteção |
TrailingGapPoints |
Compensação entre o preço de mercado e o trailing stop. |
| Filtros |
MaxSpreadPoints |
Spread máximo permitido (em etapas de preço) para abertura de novas posições. |
| Filtros |
SlippagePoints |
Configuração de deslizamento informativo (não aplicada automaticamente). |
| Gestão de Risco |
MoneyRiskMode |
Escolha entre perda fixa de moeda ou porcentagem do valor do portfólio. |
| Gestão de Risco |
RiskValue |
Quantidade de risco (moeda ou porcentagem dependendo da modalidade). |
| Geral |
TradeComment |
Comentário informativo anexado aos pedidos gerados. |
| Geral |
CandleType |
Série de velas conduzindo o ciclo de decisão. |
Notas
- A estratégia depende de assinaturas de dados de mercado para velas, cotações e negociações de Nível 1. Certifique-se de que o tipo de dados selecionado esteja disponível para a segurança escolhida.
- A lógica de trailing stop reflete a implementação MQL: ela é ativada após o preço ganhar
TrailingStartPoints + TrailingGapPoints passos e então segue o preço a uma distância de TrailingGapPoints.
- A gestão de risco compara o PnL flutuante com o limite de perda configurado e liquida a posição quando o limite é violado.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Randomized trading strategy converted from the "RRS Randomness in Nature" MQL expert advisor.
/// The strategy opens random market orders with optional trailing, stop-loss, take-profit and risk protection.
/// </summary>
public class RrsRandomnessStrategy : Strategy
{
private readonly StrategyParam<TradingModes> _tradingMode;
private readonly StrategyParam<decimal> _minVolume;
private readonly StrategyParam<decimal> _maxVolume;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<decimal> _trailingStartPoints;
private readonly StrategyParam<decimal> _trailingGapPoints;
private readonly StrategyParam<decimal> _maxSpreadPoints;
private readonly StrategyParam<decimal> _slippagePoints;
private readonly StrategyParam<RiskModes> _riskMode;
private readonly StrategyParam<decimal> _riskValue;
private readonly StrategyParam<string> _tradeComment;
private readonly StrategyParam<DataType> _candleType;
private int _tradeCounter;
private decimal? _trailingStopPrice;
private bool _openLongNext;
private decimal _entryPrice;
/// <summary>
/// Trading direction selection logic.
/// </summary>
public TradingModes Mode
{
get => _tradingMode.Value;
set => _tradingMode.Value = value;
}
/// <summary>
/// Minimal order volume.
/// </summary>
public decimal MinVolume
{
get => _minVolume.Value;
set => _minVolume.Value = value;
}
/// <summary>
/// Maximal order volume.
/// </summary>
public decimal MaxVolume
{
get => _maxVolume.Value;
set => _maxVolume.Value = value;
}
/// <summary>
/// Take-profit distance expressed in price steps.
/// </summary>
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Stop-loss distance expressed in price steps.
/// </summary>
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Profit distance that enables the trailing stop.
/// </summary>
public decimal TrailingStartPoints
{
get => _trailingStartPoints.Value;
set => _trailingStartPoints.Value = value;
}
/// <summary>
/// Trailing stop offset from current price measured in price steps.
/// </summary>
public decimal TrailingGapPoints
{
get => _trailingGapPoints.Value;
set => _trailingGapPoints.Value = value;
}
/// <summary>
/// Maximal spread allowed for opening trades (price steps).
/// </summary>
public decimal MaxSpreadPoints
{
get => _maxSpreadPoints.Value;
set => _maxSpreadPoints.Value = value;
}
/// <summary>
/// Slippage tolerance in price steps (informational parameter).
/// </summary>
public decimal SlippagePoints
{
get => _slippagePoints.Value;
set => _slippagePoints.Value = value;
}
/// <summary>
/// Risk management mode.
/// </summary>
public RiskModes MoneyRiskMode
{
get => _riskMode.Value;
set => _riskMode.Value = value;
}
/// <summary>
/// Risk value in account currency or percent depending on the mode.
/// </summary>
public decimal RiskValue
{
get => _riskValue.Value;
set => _riskValue.Value = value;
}
/// <summary>
/// Trade comment stored for informational purposes.
/// </summary>
public string TradeComment
{
get => _tradeComment.Value;
set => _tradeComment.Value = value;
}
/// <summary>
/// Candle type used to schedule strategy checks.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="RrsRandomnessStrategy"/> class.
/// </summary>
public RrsRandomnessStrategy()
{
_tradingMode = Param(nameof(Mode), TradingModes.DoubleSide)
.SetDisplay("Trading Mode", "Select whether a trade is chosen every cycle or only on random matches.", "General");
_minVolume = Param(nameof(MinVolume), 0.01m)
.SetGreaterThanZero()
.SetDisplay("Min Volume", "Minimal volume for a market order.", "Lot Settings");
_maxVolume = Param(nameof(MaxVolume), 0.5m)
.SetGreaterThanZero()
.SetDisplay("Max Volume", "Maximum volume for a market order.", "Lot Settings");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 2000m)
.SetNotNegative()
.SetDisplay("Take Profit", "Take-profit distance in price steps.", "Protection");
_stopLossPoints = Param(nameof(StopLossPoints), 3000m)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss distance in price steps.", "Protection");
_trailingStartPoints = Param(nameof(TrailingStartPoints), 1500m)
.SetNotNegative()
.SetDisplay("Trailing Start", "Profit distance that enables the trailing stop.", "Protection");
_trailingGapPoints = Param(nameof(TrailingGapPoints), 1000m)
.SetNotNegative()
.SetDisplay("Trailing Gap", "Offset between current price and trailing stop.", "Protection");
_maxSpreadPoints = Param(nameof(MaxSpreadPoints), 100m)
.SetNotNegative()
.SetDisplay("Max Spread", "Maximum spread allowed for new trades (price steps).", "Filters");
_slippagePoints = Param(nameof(SlippagePoints), 3m)
.SetNotNegative()
.SetDisplay("Slippage", "Expected slippage in price steps. Used for reference only.", "Filters");
_riskMode = Param(nameof(MoneyRiskMode), RiskModes.BalancePercentage)
.SetDisplay("Risk Mode", "Choose whether risk is fixed or percentage based.", "Risk Management");
_riskValue = Param(nameof(RiskValue), 5m)
.SetNotNegative()
.SetDisplay("Risk Value", "Risk amount in currency or percent.", "Risk Management");
_tradeComment = Param(nameof(TradeComment), "RRS")
.SetDisplay("Trade Comment", "Informational comment attached to generated orders.", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle type used to trigger the strategy logic.", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_tradeCounter = 0;
_trailingStopPrice = null;
_openLongNext = true;
_entryPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
SubscribeCandles(CandleType)
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var price = candle.ClosePrice;
ApplyProtection(price);
ApplyTrailing(price);
TryOpenTrade();
}
private void ApplyProtection(decimal marketPrice)
{
if (Position == 0)
return;
var priceStep = Security.PriceStep ?? 0.0001m;
if (priceStep <= 0m)
priceStep = 0.0001m;
var entryPrice = _entryPrice;
if (entryPrice <= 0m)
return;
if (Position > 0)
{
if (StopLossPoints > 0m)
{
var stopPrice = entryPrice - StopLossPoints * priceStep;
if (marketPrice <= stopPrice)
{
SellMarket(Math.Abs(Position));
_trailingStopPrice = null;
return;
}
}
if (TakeProfitPoints > 0m)
{
var takePrice = entryPrice + TakeProfitPoints * priceStep;
if (marketPrice >= takePrice)
{
SellMarket(Math.Abs(Position));
_trailingStopPrice = null;
}
}
}
else if (Position < 0)
{
if (StopLossPoints > 0m)
{
var stopPrice = entryPrice + StopLossPoints * priceStep;
if (marketPrice >= stopPrice)
{
BuyMarket(Math.Abs(Position));
_trailingStopPrice = null;
return;
}
}
if (TakeProfitPoints > 0m)
{
var takePrice = entryPrice - TakeProfitPoints * priceStep;
if (marketPrice <= takePrice)
{
BuyMarket(Math.Abs(Position));
_trailingStopPrice = null;
}
}
}
}
private void ApplyTrailing(decimal marketPrice)
{
if (Position == 0 || TrailingGapPoints <= 0m || TrailingStartPoints <= 0m)
return;
var priceStep = Security.PriceStep ?? 0m;
if (priceStep <= 0m)
return;
var entryPrice = _entryPrice;
if (entryPrice <= 0m)
return;
var gap = TrailingGapPoints * priceStep;
var triggerDistance = (TrailingStartPoints + TrailingGapPoints) * priceStep;
if (Position > 0)
{
var profit = marketPrice - entryPrice;
if (profit > triggerDistance)
{
var candidate = marketPrice - gap;
if (_trailingStopPrice == null || candidate > _trailingStopPrice)
_trailingStopPrice = candidate;
}
if (_trailingStopPrice != null && marketPrice <= _trailingStopPrice)
{
SellMarket(Math.Abs(Position));
_trailingStopPrice = null;
}
}
else if (Position < 0)
{
var profit = entryPrice - marketPrice;
if (profit > triggerDistance)
{
var candidate = marketPrice + gap;
if (_trailingStopPrice == null || candidate < _trailingStopPrice)
_trailingStopPrice = candidate;
}
if (_trailingStopPrice != null && marketPrice >= _trailingStopPrice)
{
BuyMarket(Math.Abs(Position));
_trailingStopPrice = null;
}
}
}
/// <inheritdoc />
protected override void OnOwnTradeReceived(MyTrade trade)
{
base.OnOwnTradeReceived(trade);
if (Position != 0m && _entryPrice == 0m)
_entryPrice = trade.Trade.Price;
if (Position == 0m)
_entryPrice = 0m;
}
private void ClosePosition()
{
var volume = Math.Abs(Position);
if (volume <= 0m)
return;
if (Position > 0)
SellMarket(volume);
else if (Position < 0)
BuyMarket(volume);
}
private void TryOpenTrade()
{
if (Position != 0)
return;
if (_openLongNext)
{
BuyMarket(Volume);
}
else
{
SellMarket(Volume);
}
_openLongNext = !_openLongNext;
_tradeCounter++;
}
/// <summary>
/// Trading mode options.
/// </summary>
public enum TradingModes
{
/// <summary>
/// Alternate between long and short entries every cycle.
/// </summary>
DoubleSide,
/// <summary>
/// Enter only when the random generator matches specific values.
/// </summary>
OneSide,
}
/// <summary>
/// Risk management configuration.
/// </summary>
public enum RiskModes
{
/// <summary>
/// Risk is defined as a fixed currency value.
/// </summary>
FixedMoney,
/// <summary>
/// Risk is calculated as a percentage of the portfolio value.
/// </summary>
BalancePercentage,
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class rrs_randomness_strategy(Strategy):
def __init__(self):
super(rrs_randomness_strategy, self).__init__()
self._tp_points = self.Param("TakeProfitPoints", 2000.0).SetNotNegative().SetDisplay("Take Profit", "TP in price steps", "Protection")
self._sl_points = self.Param("StopLossPoints", 3000.0).SetNotNegative().SetDisplay("Stop Loss", "SL in price steps", "Protection")
self._trailing_start = self.Param("TrailingStartPoints", 1500.0).SetNotNegative().SetDisplay("Trailing Start", "Profit to enable trailing", "Protection")
self._trailing_gap = self.Param("TrailingGapPoints", 1000.0).SetNotNegative().SetDisplay("Trailing Gap", "Trailing offset", "Protection")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Candle timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(rrs_randomness_strategy, self).OnReseted()
self._trailing_stop = None
self._open_long_next = True
self._entry_price = 0
def OnStarted2(self, time):
super(rrs_randomness_strategy, self).OnStarted2(time)
self._trailing_stop = None
self._open_long_next = True
self._entry_price = 0
self._step = 0.0001
if self.Security is not None and self.Security.PriceStep is not None and self.Security.PriceStep > 0:
self._step = float(self.Security.PriceStep)
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self.OnProcess).Start()
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
self._apply_protection(close)
self._apply_trailing(close)
if self.Position != 0:
return
if self._open_long_next:
self.BuyMarket()
self._entry_price = close
else:
self.SellMarket()
self._entry_price = close
self._open_long_next = not self._open_long_next
def _apply_protection(self, price):
if self.Position == 0 or self._entry_price <= 0:
return
step = self._step
if self.Position > 0:
if self._sl_points.Value > 0:
sl = self._entry_price - self._sl_points.Value * step
if price <= sl:
self.SellMarket()
self._trailing_stop = None
return
if self._tp_points.Value > 0:
tp = self._entry_price + self._tp_points.Value * step
if price >= tp:
self.SellMarket()
self._trailing_stop = None
elif self.Position < 0:
if self._sl_points.Value > 0:
sl = self._entry_price + self._sl_points.Value * step
if price >= sl:
self.BuyMarket()
self._trailing_stop = None
return
if self._tp_points.Value > 0:
tp = self._entry_price - self._tp_points.Value * step
if price <= tp:
self.BuyMarket()
self._trailing_stop = None
def _apply_trailing(self, price):
if self.Position == 0 or self._trailing_gap.Value <= 0 or self._trailing_start.Value <= 0:
return
step = self._step
gap = self._trailing_gap.Value * step
trigger = (self._trailing_start.Value + self._trailing_gap.Value) * step
if self.Position > 0:
profit = price - self._entry_price
if profit > trigger:
candidate = price - gap
if self._trailing_stop is None or candidate > self._trailing_stop:
self._trailing_stop = candidate
if self._trailing_stop is not None and price <= self._trailing_stop:
self.SellMarket()
self._trailing_stop = None
elif self.Position < 0:
profit = self._entry_price - price
if profit > trigger:
candidate = price + gap
if self._trailing_stop is None or candidate < self._trailing_stop:
self._trailing_stop = candidate
if self._trailing_stop is not None and price >= self._trailing_stop:
self.BuyMarket()
self._trailing_stop = None
def CreateClone(self):
return rrs_randomness_strategy()