Auf GitHub ansehen

Zufällige vollständige Strategie

Überblick

Die At Random Full Strategy ist eine originalgetreue Umsetzung des MetaTrader 5 Expertenberaters „At random Full“. Es hält die Ursprüngliche Idee, Trades auf der Grundlage eines Zufallsgenerators zu eröffnen und gleichzeitig die gleichen Money-Management-Wechsel offenzulegen: Richtung Filter, Rasterabstände, optionale Zeitfenster und ein Ein-/Ausschalter für die Mittelwertbildung. Der Port StockSharp verwendet den High-Level-Port API, Daher wird die gesamte Entscheidungsschleife durch Kerzenabonnements und standardmäßige StartProtection-Helfer für Schutzanordnungen gesteuert.

Handelslogik

  1. Bei jeder abgeschlossenen Kerze überprüft die Strategie, ob der Handel zulässig ist (Sitzungsfilter, Portfoliostatus und optional). Flag „nur eine Position“).
  2. Ein Pseudozufallsgenerator entscheidet zwischen einem Long- oder Short-Einstieg. Der Parameter ReverseSignals kann das Ergebnis umdrehen emulieren den MQL-Umkehrmodus.
  3. Richtungsfilter (TradeMode) blockieren unerwünschte Signale. Der Code erzwingt auch die ursprüngliche EA-Regel eines einzelnen Trades pro Bar in jede Richtung, indem Sie sich die Kerzenöffnungszeit des letzten Signals merken.
  4. Die Rasterverwaltungsoptionen spiegeln das Verhalten von MetaTrader wider:
    • MaxPositions begrenzt die Anzahl der gemittelten Einträge pro Seite.
    • MinStepPoints erfordert einen Mindestabstand (umgerechnet unter Verwendung der Sicherheitspreisstufe in einen Preis) zwischen aufeinanderfolgenden Einträgen.
    • CloseOpposite erzwingt die Schließung des bestehenden Gegenrisikos, bevor ein neuer Handel gesendet wird.
  5. Marktaufträge werden über BuyMarket / SellMarket mit einem normalisierten Volumen erteilt, das durch OrderVolume definiert ist.

Positions- und Risikomanagement

  • StartProtection fügt Stop-Loss- und Take-Profit-Orders hinzu, die den MetaTrader-Eingaben entsprechen. Wenn TrailingStopPoints ist größer als Null ist, wird der integrierte StockSharp-Trailing-Modus aktiviert. Die Parameter TrailingActivatePoints und TrailingStepPoints werden aus Gründen der Transparenz in Preisabstände umgewandelt und protokolliert, das tatsächliche Nachziehen wird jedoch von der verwaltet Plattform.
  • Alle Volumenberechnungen berücksichtigen die Austauschmetadaten (Minimum, Maximum und Schritt) genau wie die MQL-Hilfsroutinen.
  • Die Zeitsteuerung emuliert den Block InpTimeControl aus dem Skript. Wenn diese Option aktiviert ist, sind Trades nur innerhalb des konfigurierten Bereichs zulässig [SessionStart, SessionEnd] Fenster; Übernachtungssitzungen werden unterstützt.

Parameter

Parameter Beschreibung Standard
CandleType Kerzenserien, die zum Planen der Entscheidungsschleife verwendet werden. 15 minute timeframe
OrderVolume Basis-Market-Order-Volumen in Lots. 0.1
MaxPositions Maximale Anzahl gemittelter Einträge pro Richtung (0 = unbegrenzt). 5
MinStepPoints Mindestabstand zwischen Einträgen, ausgedrückt in MetaTrader Punkten. 150
StopLossPoints Stop-Loss-Distanz in Punkten. 150
TakeProfitPoints Take-Profit-Distanz in Punkten. 460
TrailingActivatePoints Gewinnschwelle (in Punkten), die zu Informationszwecken protokolliert wird, wenn das Trailing aktiviert ist. 70
TrailingStopPoints Trailing-Stop-Distanz wurde an StartProtection übergeben. 250
TrailingStepPoints Schritt zwischen Nachlaufanpassungen, protokolliert neben der Aktivierungsdistanz. 50
OnlyOnePosition Blockiert neue Trades, bis die aktuelle Nettoposition geschlossen ist. false
CloseOpposite Schließt das entgegengesetzte Risiko, bevor ein Handel eröffnet wird. false
ReverseSignals Kehrt die zufällige Entscheidung um, sodass Käufe zu Verkäufen werden und umgekehrt. false
UseTimeControl Aktiviert den Zeitfilter für Handelssitzungen. false
SessionStart Sitzungsstartzeit (einschließlich), wenn UseTimeControl true ist. 10:01
SessionEnd Sitzungsendzeit (einschließlich), wenn UseTimeControl true ist. 15:02
Mode Zulässige Handelsrichtung (Both, BuyOnly, SellOnly). Both
RandomSeed Optionaler deterministischer Startwert für den Pseudozufallsgenerator (0 = Anzahl der Umgebungsticks). 0

Implementierungshinweise

  • Alle Kommentare sind auf Englisch verfasst und der Code verwendet Tabulatoreinrückungen, entsprechend den Repository-Richtlinien.
  • Die Kerzenverarbeitung basiert auf SubscribeCandles().Bind(...) und stellt sicher, dass die Logik wie in EA einmal pro fertigem Balken ausgeführt wird.
  • Die Strategie verfolgt die letzten Kauf- und Verkaufsfüllpreise, um die Mindestabstandsbeschränkung auch während der Mittelwertbildung durchzusetzen.
  • Protokollierungsanweisungen spiegeln die detaillierten Diagnosen wider, die das Originalskript ausgibt: Jeder Eintrag gibt die gewählte Richtung bekannt. Einstiegspreis, Volumen und die nachfolgende Konfiguration beim Start.

Nutzungstipps

  • Da das Handelssignal zufällig ist, eignet sich die Strategie am besten zum Testen der Infrastruktur oder zur Demonstration von Risikokontrollen.
  • Passen Sie OrderVolume, StopLossPoints und TakeProfitPoints an, um sie an die Tick-Größe und Volatilität des von Ihnen verwendeten Instruments anzupassen planen zu handeln.
  • Aktivieren Sie UseTimeControl, wenn EA nur während einer bestimmten Sitzung (z. B. der Sitzung in London oder New York) funktionieren soll.
  • Verwenden Sie RandomSeed während Optimierungsläufen, um reproduzierbare Sequenzen zufälliger Entscheidungen zu erreichen.
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();
	}
}