Открыть на GitHub

Стратегия VirtPO TestBed Scalp

Стратегия переносит эксперта VirtPOTestBed_ScalpM1 из MetaTrader 4 на высокоуровневый API StockSharp. Основная идея сохранена: создаются виртуальные отложенные ордера, которые активируются при пересечениях стохастика и исполняются, когда цена подтверждает импульс. Все фильтры, правила управления позицией и расписание из оригинала реализованы средствами StockSharp.

Основная логика

  1. Виртуальные отложенные ордера. При отсутствии позиции на каждом закрывшемся баре выполняется блок фильтров:

    • спред не должен превышать SpreadMaxPips (используются котировки Level1);
    • средний тиковый объём за три последних бара больше VolumeLimit;
    • средняя абсолютная волатильность (размер тела баров за VolatilityPeriod) превышает VolatilityLimit;
    • ширина полос Боллинджера (период BollingerPeriod, ширина 2) находится в диапазоне BollingerLowerLimitBollingerUpperLimit;
    • текущее время входит в торговое окно (EntryHour + OpenHours) и не попадает в запрещённые дни (Day1, Day2, пятничный FridayEndHour);
    • разница между быстрой и медленной SMA в пунктах превышает SmaDifferencePips по модулю;
    • тело предыдущего бара меньше LastBarLimitPips.

    После прохождения фильтров анализируются пересечения стохастика:

    • пересечение вверх через StochasticSetLevel активирует виртуальный buy stop на расстоянии PoThresholdPips выше bid;
    • пересечение вниз через 100 - StochasticSetLevel активирует виртуальный sell stop на таком же расстоянии ниже bid. Для каждого виртуального ордера сохраняются время истечения (PoTimeLimitMinutes) и расстояния стопа / тейк-профита (StopLossPips, TakeProfitPips).
  2. Исполнение. Если включён флаг TickLevel, стратегия отслеживает поток сделок и исполняет виртуальные ордера сразу после пробоя уровня. При TickLevel = false проверка выполняется на закрытии бара. Когда цена пересекает виртуальный уровень, отправляется рыночная заявка, а виртуальный ордер удаляется.

  3. Риск-менеджмент. После открытия позиции отслеживаются:

    • фиксированные stop-loss и take-profit в пунктах от цены входа;
    • опциональный трейлинг-стоп TrailingStopPips по экстремуму с начала сделки;
    • максимальное время удержания CloseTimeMinutes с фильтром ProfitType (0 — закрыть всё, 1 — только прибыль, 2 — только убыток).

Все расстояния переводятся в цену по PriceStep инструмента с учётом пятизначных котировок, как в MQL. Параметр OrderVolume задаёт объём рыночной заявки. После закрытия позиции состояние стратегии сбрасывается.

Важные особенности

  • Для расчёта спреда и триггеров необходим поток Level1; без лучшего bid/ask фильтры блокируют сделки.
  • Режим TickLevel повторяет поведение эксперта: при отключении исполнение происходит только на закрытии баров, что удобнее для тестирования.
  • Стратегия работает с одной совокупной позицией, аналогично ограничению количества ордеров в MT4.

Параметры

Группа Параметр Описание
General Candle Type Тип свечей (по умолчанию 1 минута).
Execution Tick Level Использовать поток сделок для немедленного исполнения виртуальных ордеров.
Execution PO Threshold (pips) Расстояние от bid до уровня виртуального стопа.
Execution PO Lifetime (min) Время жизни виртуального ордера.
Filters Max Spread (pips) Максимально допустимый спред.
Filters Volume Limit Минимальный средний тиковый объём.
Filters Volatility Period Количество баров для расчёта волатильности.
Filters Volatility Limit Минимальный размер тела бара в пунктах.
Filters Bollinger Period Период полос Боллинджера.
Filters Bollinger Lower / Upper Допустимая ширина полос в пунктах.
Filters Last Bar Limit Максимальное тело предыдущего бара.
Trend Fast/Slow SMA Периоды средних для фильтра тренда.
Trend SMA Difference Минимальная разница SMA в пунктах.
Stochastic %K / %D / Smooth Параметры стохастика.
Stochastic Stochastic Set Уровень активации виртуального ордера.
Stochastic Stochastic Go Уровень исполнения виртуального ордера.
Trading Order Volume Базовый объём сделки.
Risk Take Profit / Stop Loss / Trailing Stop Дистанции выходов в пунктах.
Schedule Disable Days, Day1, Day2 Настройки запрета по дням недели (99 — отключить).
Schedule Entry Hour / Open Hours Старт и длительность торгового окна.
Schedule Friday Cut-off Час завершения торговли в пятницу.
Risk Max Lifetime Ограничение по времени удержания позиции (≥5000 — выключить).
Risk Profit Filter 0 — закрыть любую позицию, 1 — только прибыльную, 2 — только убыточную после таймера.

Отличия от оригинала

  • Класс MQL CPO заменён внутренним состоянием, которое вызывает BuyMarket/SellMarket при пробое уровня.
  • Стопы и тейк-профиты проверяются по максимумам/минимумам свечи (в тестах) либо по тикам, если они доступны. Частичное исполнение и хеджевые позиции не поддерживаются.
  • Алгоритм автолота GLots не переносился — в StockSharp используется фиксированный OrderVolume.

Такое упрощение сохраняет торговую идею и вписывается в модель StockSharp с единой позицией.

namespace StockSharp.Samples.Strategies;

using System;

using Ecng.Common;

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

/// <summary>
/// Virtual Pending Order Scalp strategy: ATR-based breakout scalper.
/// Buys when price breaks above recent high + ATR offset. Sells on break below low - ATR.
/// </summary>
public class VirtPoTestBedScalpStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _atrPeriod;

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

	public int AtrPeriod
	{
		get => _atrPeriod.Value;
		set => _atrPeriod.Value = value;
	}

	public VirtPoTestBedScalpStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");

		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("ATR Period", "ATR period for breakout offset", "Indicators");
	}

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

		var atr = new AverageTrueRange { Length = AtrPeriod };

		decimal? prevHigh = null;
		decimal? prevLow = null;

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

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				var close = candle.ClosePrice;

				if (prevHigh.HasValue && prevLow.HasValue)
				{
					if (close > prevHigh.Value + atrVal * 0.5m && Position <= 0)
						BuyMarket();
					else if (close < prevLow.Value - atrVal * 0.5m && Position >= 0)
						SellMarket();
				}

				prevHigh = candle.HighPrice;
				prevLow = candle.LowPrice;
			})
			.Start();

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