Открыть на GitHub

Стратегия случайного направления

Обзор

Стратегия представляет собой порт MetaTrader 5 советника "At random" (MQL ID 39835) на платформу StockSharp. Оригинальный робот демонстрирует поведение полностью случайного процесса принятия решений, когда алгоритм всё время должен находиться в рынке. Каждая завершённая свеча инициирует подбрасывание монеты, которое решает, покупать или продавать дальше. Версия для StockSharp сохраняет ту же идею, но реализована через высокоуровневые API (SubscribeCandles, BuyMarket, SellMarket), поэтому легко запускается в Designer или Runner.

В соответствии с исходным MQL-скриптом здесь отсутствуют тейк-профит, стоп-лосс и трейлинг. Перенос служит скорее учебным примером и тестовым стендом инфраструктуры, чем работоспособной торговой системой.

Логика торговли

  1. Подписка на выбранный поток свечей (CandleType). По умолчанию используется интервал 15 минут, чтобы повторить режим "текущий таймфрейм" в MetaTrader.
  2. После закрытия свечи стратегия проверяет, нужно ли закрыть прежнюю позицию. Если CloseBeforeReversal включён, позиция закрывается и алгоритм ждёт, пока она действительно станет нулевой, прежде чем открывать следующую сделку.
  3. С помощью псевдослучайного генератора выбирается направление сделки. Параметр RandomSeed даёт возможность фиксировать последовательность для воспроизводимых тестов.
  4. По фиксированному объёму TradeVolume отправляется рыночный приказ. Лонги и шорты симметричны, защитные ордера не выставляются. Опциональный флаг LogSignals включает запись каждого случайного решения в журнал через AddInfoLog.

Поскольку на каждой свече формируется только одно решение, стратегия либо находится вне позиции, либо держит ровно одну сделку. Разворот или закрытие происходят только на следующем баре.

Управление ордерами и рисками

  • Все операции выполняются рыночными приказами BuyMarket / SellMarket с заданным объёмом. Лимитных и стоп-заявок нет.
  • Если отключить CloseBeforeReversal, сделки могут следовать без паузы: новый сигнал сразу открывает противоположное направление без явного закрытия предыдущего ордера.
  • Деньжного менеджмента и защиты счёта не реализовано — цель порта состоит в воспроизведении поведения оригинального советника для образовательных и инфраструктурных задач.

Параметры

Параметр Описание
TradeVolume Базовый объём каждой случайной сделки; должен быть положительным.
CloseBeforeReversal Принудительно закрывать текущую позицию перед открытием следующего случайного ордера.
LogSignals Записывать в журнал сообщение при генерации очередного случайного направления.
CandleType Таймфрейм, на основе которого запускается "подбрасывание монеты".
RandomSeed Семя псевдослучайного генератора. Значение 0 — использовать системное время.

Рекомендации по использованию

  • Как и в MQL-версии, стопы и тейки отсутствуют. При любых экспериментах с реальными средствами риск-контроль необходимо добавлять самостоятельно.
  • Фиксированное семя удобно для получения воспроизводимых результатов при оптимизации или сравнении случайных сценариев.
  • Во время тестов полезно включать журналирование, потому что чисто случайная стратегия даёт мало наглядной информации на графике.
using System;

using Ecng.Common;

using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy that randomly opens long or short positions based on a random threshold.
/// Mirrors the "At random" MetaTrader expert.
/// </summary>
public class AtRandomStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _randomSeed;

	private Random _random;
	private int _barCount;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int RandomSeed
	{
		get => _randomSeed.Value;
		set => _randomSeed.Value = value;
	}

	public AtRandomStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe that triggers random decisions", "Data");

		_randomSeed = Param(nameof(RandomSeed), 42)
			.SetDisplay("Random Seed", "Seed for the pseudo random generator (0 = system clock)", "Diagnostics");
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_random = RandomSeed == 0 ? new Random() : new Random(RandomSeed);
		_barCount = 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;

		_barCount++;

		// Only trade occasionally to keep turnover within runner limits.
		if (_random.Next(0, 6) != 0)
			return;

		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		// Random direction
		var goLong = _random.Next(0, 2) == 0;

		if (goLong)
		{
			if (Position <= 0)
				BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
		}
		else
		{
			if (Position >= 0)
				SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
		}
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_random = null;
		_barCount = 0;

		base.OnReseted();
	}
}