Die Neue Zufallsstrategie emuliert den ursprünglichen MetaTrader-Experten "New Random" und bietet drei verschiedene Einstiegs-Auswahlmodi. Sie öffnet immer nur eine einzige Position und wartet, bis die aktuelle Position geschlossen ist, bevor sie die nächste Orderrichtung generiert. Markteinstiege werden bei Top-of-Book-Updates (Level-1-Daten) ausgelöst und verwenden die besten Bid/Ask-Kurse als Ausführungsanker. Die Strategie berechnet automatisch Stop-Loss- und Take-Profit-Abstände in Pips und passt sich an 3- und 5-stellige Forex-Kurse genauso wie die MQL-Version an.
Einstiegsmodi
Generator – Die nächste Richtung wird durch einen Pseudozufallsgenerator gewählt, der beim Strategiestart geseeded wird. Jede Gelegenheit ist ein unabhängiger Münzwurf zwischen Kauf und Verkauf.
Kauf-Verkauf-Kauf – Positionen wechseln strikt zwischen Kauf und Verkauf. Die allererste Order ist ein Kauf, gefolgt von einem Verkauf, und so weiter.
Verkauf-Kauf-Verkauf – Positionen wechseln strikt, beginnend mit einem Verkauf, gefolgt von einem Kauf, und wiederholen sich.
Parameter
Random Mode (Mode) – Wählt einen der drei oben beschriebenen Einstiegsmechanismen aus. Standardmäßig der Zufallsgenerator.
Minimal Lot Count (MinimalLotCount) – Multipliziert das minimale handelbare Volumen des Instruments. Ein Wert von 1 bedeutet, dass die Strategie genau Security.VolumeMin handelt, während höhere Werte die Ordergröße um ganze Vielfache skalieren.
Stop Loss (pips) (StopLossPips) – Abstand in Pips unterhalb/oberhalb des ausgeführten Preises, bei dem die Strategie die Position beendet. Auf 0 setzen, um den Stop-Loss zu deaktivieren.
Take Profit (pips) (TakeProfitPips) – Abstand in Pips, bei dem die Strategie Gewinne realisiert. Auf 0 setzen, um den Take-Profit zu deaktivieren.
Handelslogik
Abonniert Level-1-Daten für das konfigurierte Wertpapier und speichert ständig die neuesten Bid-, Ask- und letzten Handelskurse.
Wenn keine Position offen und keine Order ausstehend ist, wertet die Strategie den ausgewählten Modus aus, um die nächste Richtung zu bestimmen.
Orders werden am Markt platziert, wobei der neueste beste Bid/Ask-Schnappschuss verwendet wird. Die Stop-Loss- und Take-Profit-Ziele werden sofort aus dem Einstiegskurs unter Verwendung der Pip-Distanzparameter berechnet.
Es darf nur eine einzige Position gleichzeitig existieren. Nachfolgende Einstiege werden unterdrückt, bis die aktive Position vollständig geschlossen ist.
Positionsverwaltung
Long-Positionen werden vorzeitig beendet, wenn der aktuelle Kurs auf den Stop-Loss fällt oder darunter, oder auf den Take-Profit steigt oder darüber.
Short-Positionen werden beendet, wenn der aktuelle Kurs auf den Stop-Loss steigt oder darüber, oder auf den Take-Profit fällt oder darunter.
Kursvergleiche verwenden immer die frischesten Level-1-Informationen: den letzten Handelskurs, wenn verfügbar, andernfalls den besten Bid/Ask für die jeweilige Seite.
Nach dem Schließen eines Trades setzt die Strategie den internen Zustand zurück, wechselt optional die nächste Richtung (für Sequenzmodi) und wartet auf das nächste Kursupdate, bevor sie wieder einsteigt.
Hinweise
Die Strategie pyramidiert nie Positionen und hält das Verhalten für sequenzbasierte Modi deterministisch.
Der Zufallsmodus wird mit dem aktuellen Tick-Zähler geseeded, so dass jeder Lauf einen einzigartigen Orderstrom erzeugt.
Alle internen Kommentare und Protokolle sind auf Englisch, um den Repository-Richtlinien zu entsprechen.
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 entry strategy that mimics the MetaTrader "New Random" expert.
/// </summary>
public class NewRandomStrategy : Strategy
{
/// <summary>
/// Available direction selection modes.
/// </summary>
public enum RandomModes
{
/// <summary>Use a pseudo random generator for every entry decision.</summary>
Generator,
/// <summary>Alternate buy-sell-buy.</summary>
BuySellBuy,
/// <summary>Alternate sell-buy-sell.</summary>
SellBuySell
}
private readonly StrategyParam<RandomModes> _mode;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private readonly StrategyParam<DataType> _candleType;
private Sides? _sequenceLastSide;
private Sides? _positionSide;
private decimal _entryPrice;
private int _candleCount;
/// <summary>Direction selection mode.</summary>
public RandomModes Mode
{
get => _mode.Value;
set => _mode.Value = value;
}
/// <summary>Stop loss in price steps.</summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>Take profit in price steps.</summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>Candle type.</summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public NewRandomStrategy()
{
_mode = Param(nameof(Mode), RandomModes.Generator)
.SetDisplay("Random Mode", "Direction selection mode", "General");
_stopLossPoints = Param(nameof(StopLossPoints), 5)
.SetGreaterThanZero()
.SetDisplay("Stop Loss (pts)", "Stop loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 5)
.SetGreaterThanZero()
.SetDisplay("Take Profit (pts)", "Take profit in price steps", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sequenceLastSide = null;
_positionSide = null;
_entryPrice = 0m;
_candleCount = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_sequenceLastSide = Mode switch
{
RandomModes.BuySellBuy => Sides.Sell,
RandomModes.SellBuySell => Sides.Buy,
_ => null
};
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;
_candleCount++;
if (_candleCount < 3)
return;
var step = Security?.PriceStep ?? 1m;
var stopDistance = StopLossPoints * step;
var takeDistance = TakeProfitPoints * step;
var price = candle.ClosePrice;
// Check SL/TP for current position
if (Position != 0 && _entryPrice > 0)
{
var hit = false;
if (_positionSide == Sides.Buy)
{
if (stopDistance > 0 && candle.LowPrice <= _entryPrice - stopDistance)
hit = true;
if (takeDistance > 0 && candle.HighPrice >= _entryPrice + takeDistance)
hit = true;
}
else if (_positionSide == Sides.Sell)
{
if (stopDistance > 0 && candle.HighPrice >= _entryPrice + stopDistance)
hit = true;
if (takeDistance > 0 && candle.LowPrice <= _entryPrice - takeDistance)
hit = true;
}
if (hit)
{
if (Position > 0)
SellMarket();
else if (Position < 0)
BuyMarket();
_positionSide = null;
_entryPrice = 0m;
}
}
// If flat, open new random position
if (Position == 0 && _positionSide == null)
{
var side = DetermineNextSide();
if (side == Sides.Buy)
BuyMarket();
else
SellMarket();
_positionSide = side;
_entryPrice = price;
_sequenceLastSide = side;
}
}
private Sides DetermineNextSide()
{
// All modes use deterministic alternating logic
return _sequenceLastSide == Sides.Buy ? Sides.Sell : Sides.Buy;
}
}
import clr
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 new_random_strategy(Strategy):
"""Alternating entry strategy with SL/TP that mimics the MetaTrader New Random expert."""
def __init__(self):
super(new_random_strategy, self).__init__()
self._stop_loss_points = self.Param("StopLossPoints", 5) \
.SetGreaterThanZero() \
.SetDisplay("Stop Loss (pts)", "Stop loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 5) \
.SetGreaterThanZero() \
.SetDisplay("Take Profit (pts)", "Take profit in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._seq_last_side = 0 # 0=none, 1=buy, -1=sell
self._pos_side = 0
self._entry_price = 0.0
self._candle_count = 0
@property
def StopLossPoints(self):
return int(self._stop_loss_points.Value)
@property
def TakeProfitPoints(self):
return int(self._take_profit_points.Value)
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(new_random_strategy, self).OnStarted2(time)
self._seq_last_side = -1 # start with sell, so first entry is buy
self._pos_side = 0
self._entry_price = 0.0
self._candle_count = 0
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
self._candle_count += 1
if self._candle_count < 3:
return
sec = self.Security
step = float(sec.PriceStep) if sec is not None and sec.PriceStep is not None and float(sec.PriceStep) > 0 else 1.0
stop_dist = self.StopLossPoints * step
take_dist = self.TakeProfitPoints * step
price = float(candle.ClosePrice)
# Check SL/TP
if self.Position != 0 and self._entry_price > 0:
hit = False
if self._pos_side == 1:
if stop_dist > 0 and float(candle.LowPrice) <= self._entry_price - stop_dist:
hit = True
if take_dist > 0 and float(candle.HighPrice) >= self._entry_price + take_dist:
hit = True
elif self._pos_side == -1:
if stop_dist > 0 and float(candle.HighPrice) >= self._entry_price + stop_dist:
hit = True
if take_dist > 0 and float(candle.LowPrice) <= self._entry_price - take_dist:
hit = True
if hit:
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
self._pos_side = 0
self._entry_price = 0.0
# If flat, open new position (alternating)
if self.Position == 0 and self._pos_side == 0:
side = -1 if self._seq_last_side == 1 else 1
if side == 1:
self.BuyMarket()
else:
self.SellMarket()
self._pos_side = side
self._entry_price = price
self._seq_last_side = side
def OnReseted(self):
super(new_random_strategy, self).OnReseted()
self._seq_last_side = 0
self._pos_side = 0
self._entry_price = 0.0
self._candle_count = 0
def CreateClone(self):
return new_random_strategy()