Открыть на GitHub

Roulette Game

Стратегия Roulette Game переносит одноимённую MetaTrader-игру в StockSharp. Каждый завершённый бар воспринимается как новое вращение рулетки, направление сделки выбирается случайно, а объём позиции увеличивается после серии убыточных раундов по принципу мартингейла. Внутренние переменные стратегии отслеживают виртуальный банк и ограничивают риск за счёт настраиваемых лимитов.

Раунд начинается с закрытия открытых позиций, затем «монетка» определяет ставку на рост или падение, и отправляется рыночная заявка в выбранную сторону. После закрытия следующей свечи стратегия проверяет, оправдалась ли ставка: если цена закрылась в нужном направлении, объём сбрасывается к базовому; если нет — умножается до тех пор, пока не достигнут потолок или ограничение по количеству подряд убыточных раундов. Дополнительно можно задать паузу в несколько свечей между раундами.

Конвертация демонстрирует работу с временными циклами, хранением собственного состояния и взаимодействием с высокоуровневым API StockSharp без использования индикаторов. Это пример того, как игровые подходы к управлению капиталом можно повторить в торговой среде.

Подробности

  • Условия входа: Технических фильтров нет. Направление выбирается случайно по закрытию свечи.
  • Лонг/Шорт: Оба направления, случайный выбор каждый раунд.
  • Условия выхода: Позиция закрывается на следующей завершённой свече с оценкой результата ставки.
  • Стопы: Жёстких стоп-ордеров нет, риск контролируется лимитами на множитель и серию проигрышей.
  • Значения по умолчанию:
    • BaseVolume = 1m
    • LossMultiplier = 2m
    • MaxMultiplier = 16m
    • RoundCooldown = 1
    • MaxLosingStreak = 5
    • CandleType = TimeSpan.FromMinutes(1)
  • Фильтры:
    • Категория: Money Management
    • Направление: Оба
    • Индикаторы: Нет
    • Стопы: Нет
    • Сложность: Начальный уровень
    • Таймфрейм: Краткосрочный
    • Сезонность: Нет
    • Нейросети: Нет
    • Дивергенция: Нет
    • Уровень риска: Высокий

Примечания

  • Объём заявок рассчитывается с учётом множителя и округляется к шагу объёма инструмента.
  • При выигрыше множитель сбрасывается, при проигрыше увеличивается до заданного потолка либо до срабатывания ограничителя серии.
  • Задержка между раундами позволяет снизить частоту сделок и синхронизироваться с медленными источниками данных.
namespace StockSharp.Samples.Strategies;

using System;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Roulette Game strategy: random-like entries based on candle direction with SMA filter.
/// Buys when candle is bullish and close above SMA. Sells when bearish and below SMA.
/// </summary>
public class RouletteGameStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _smaPeriod;

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

	public int SmaPeriod
	{
		get => _smaPeriod.Value;
		set => _smaPeriod.Value = value;
	}

	public RouletteGameStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");

		_smaPeriod = Param(nameof(SmaPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("SMA Period", "SMA period", "Indicators");
	}

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

		var sma = new SimpleMovingAverage { Length = SmaPeriod };

		decimal? prevClose = null;
		decimal? prevSma = null;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(sma, (candle, smaVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				var close = candle.ClosePrice;
				var isBullish = close > candle.OpenPrice;

				if (prevClose.HasValue && prevSma.HasValue)
				{
					var crossUp = prevClose.Value <= prevSma.Value && close > smaVal;
					var crossDown = prevClose.Value >= prevSma.Value && close < smaVal;

					if (isBullish && crossUp && Position <= 0)
						BuyMarket();
					else if (!isBullish && crossDown && Position >= 0)
						SellMarket();
				}

				prevClose = close;
				prevSma = smaVal;
			})
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, sma);
			DrawOwnTrades(area);
		}
	}
}