RRS ランダム性戦略
概要
RRS ランダムネス戦略 は、MetaTrader 用の「自然界の RRS ランダムネス EA」の StockSharp 移植です。 ランダムなロングまたはショートの市場エントリーを生成することで元のエキスパートアドバイザーをエミュレートし、ストップロスとテイクプロフィットレベルを適用し、オプションで収益性の高い取引を追跡し、変動損失が設定されたしきい値を超えた場合にリスクベースの清算を実行します。
StockSharp は証券ごとにネット ポジションを使用するため、ロングとショートの同時エクスポージャーはサポートされていません。したがって、「DoubleSide」モードは、MetaTrader のように 2 つのヘッジ取引を維持するのではなく、機会ごとにエントリー方向を交互に切り替えます。
取引ロジック
- この戦略は、完成したローソク足ごとに、取引またはレベル 1 相場から取得した最新の市場価格を評価します。
- オープンポジションがある場合、ストップロス、テイクプロフィット、トレーリングストップのルールが適用され、ポートフォリオのリスクチェックが実行されます。
- フラットの場合、新しい取引を開始する前にスプレッドとボリュームの制約を検証します。
- DoubleSide モードでは、長いエントリと短いエントリが交互に切り替わります。
- OneSide モードは、元の EA ルールに従います。
[0,5]のランダムな整数は、値1または4のロングを開き、0または3のショートを開きます。
- 取引量は、設定された最小値と最大値の間で均一に描画され、商品の取引量ステップに合わせて調整されます。
パラメーター
| グループ | 名前 | 説明 |
|---|---|---|
| 一般 | Mode |
取引モード: 代替エントリー (DoubleSide) またはランダムなゲートエントリー (OneSide)。 |
| ロット設定 | MinVolume / MaxVolume |
ランダムに生成された取引のボリューム範囲。 |
| 保護 | TakeProfitPoints |
価格ステップでの利食い距離。 |
| 保護 | StopLossPoints |
価格ステップのストップロス距離。 |
| 保護 | TrailingStartPoints |
トレーリングストップ管理を可能にする利益距離。 |
| 保護 | TrailingGapPoints |
市場価格とトレーリングストップの間のオフセット。 |
| フィルター | MaxSpreadPoints |
新しいポジションをオープンする場合の最大許容スプレッド (価格ステップ単位)。 |
| フィルター | SlippagePoints |
情報スリッページ設定 (自動的には適用されません)。 |
| リスク管理 | MoneyRiskMode |
固定通貨損失またはポートフォリオ価値のパーセントのいずれかを選択します。 |
| リスク管理 | RiskValue |
リスクの量 (モードに応じて通貨またはパーセント)。 |
| 一般 | TradeComment |
生成された注文に添付される情報コメント。 |
| 一般 | CandleType |
意思決定ループを駆動するキャンドル シリーズ。 |
注意事項
- この戦略は、ローソク足、レベル 1 の相場、および取引の市場データのサブスクリプションに依存しています。選択したデータ型が選択したセキュリティで利用可能であることを確認してください。
- トレーリング ストップ ロジックは MQL の実装を反映しています。価格が
TrailingStartPoints + TrailingGapPointsステップ上昇した後にアクティブになり、その後TrailingGapPointsの距離で価格に追従します。 - リスク管理では、変動損益と設定された損失しきい値を比較し、しきい値を超えた場合にポジションを清算します。
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()