Открыть на GitHub

Стратегия FXF Fast in Fast out

Обзор

FXF Fast in Fast out — это волатильностная стратегия пробоя, переписанная с оригинального эксперта MetaTrader 4 на высокоуровневый API StockSharp. Алгоритм отслеживает выбранный таймфрейм, оценивает величину завершившейся свечи и текущий спред, после чего размещает отложенные ордера, пытаясь присоединиться к импульсу. Сигналы формируются только по закрывшимся свечам, а данные Level1 используются для контроля спреда, установки заявок и ведения трейлинг-стопа.

Если диапазон свечи превышает порог VolatilitySizePoints, стратегия сравнивает среднюю цену (между лучшими bid/ask) с ценой открытия. Средняя цена выше открытия — размещается buy stop над текущим ask; ниже — sell stop под текущим bid. К каждому отложенному ордеру добавляются стоп-лосс и тейк-профит, а при необходимости объём сделки рассчитывается автоматически исходя из капитала и процента риска. После исполнения ордера можно включить защитный трейлинг-стоп.

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

  • Определение сигнала: по каждой закрывшейся свече рассчитывается диапазон в шагах цены; если он не меньше VolatilitySizePoints, свеча рассматривается как потенциальный пробой.
  • Выбор направления: средняя цена, рассчитанная по лучшим bid/ask, сравнивается с ценой открытия. Значение выше открытия означает покупку, ниже — продажу. При равенстве или недостаточной волатильности сигнал игнорируется.
  • Фильтр спреда: стратегия постоянно отслеживает текущий спред. Если он превышает MaxSpreadPoints, новые заявки не размещаются, а активные отложенные ордера снимаются.
  • Работа с отложенными ордерами: в пределах одной свечи допускается только один ордер, причём направление соответствует последнему сигналу. Цена выставляется на расстоянии EnterOffsetPoints от текущей котировки, стоп-уровни также задаются в шагах цены.
  • Управление капиталом: при включённом UseMoneyManagement объём сделки рассчитывается по текущей стоимости портфеля, проценту риска RiskPercent и величине стоп-лосса. Если необходимые данные по инструменту недоступны или стоп-лосс равен нулю, используется базовый объём Volume.
  • Трейлинг-стоп: опция EnableTrailing активирует внутренний трейлинг, который удерживает защитный уровень на расстоянии TrailingStopPoints плюс актуальный спред. При пробое защитного уровня позиция закрывается рыночным ордером.

Параметры

Параметр Описание
EnterOffsetPoints Смещение цены отложенного ордера от лучшей котировки в шагах цены.
MaxSpreadPoints Максимально допустимый спред (в шагах цены). Превышение ведёт к отмене заявок и запрету на новые входы.
TakeProfitPoints Дистанция тейк-профита в шагах цены. Ноль — тейк-профит не ставится.
StopLossPoints Дистанция стоп-лосса в шагах цены. Ноль отключает стоп-лосс и автоматический мани-менеджмент.
VolatilitySizePoints Минимальный диапазон свечи, необходимый для генерации сигнала.
EnableTrailing Флаг включения трейлинг-стопа для открытой позиции.
TrailingStopPoints Базовая дистанция трейлинг-стопа (в шагах цены), к которой добавляется текущий спред.
UseMoneyManagement Включает динамический расчёт объёма позиции.
RiskPercent Процент капитала, который допускается рискнуть в одной сделке при активном мани-менеджменте.
MaxOrdersPerBar Максимальное количество заявок, разрешённых в рамках одной свечи (обычно 1).
CandleType Тип/таймфрейм свечей для расчётов, по умолчанию 15 минут.

Процесс работы

  1. Фиксация сигнала — по закрытии свечи с достаточной волатильностью определяется направление сделки.
  2. Проверка условий — должны быть актуальные котировки, разрешена торговля, нет открытой позиции и активных заявок.
  3. Размещение ордера — выставляется buy stop или sell stop с заданными отступами, стоп-лоссом и тейк-профитом.
  4. Сопровождение — после исполнения ордера внутренний трейлинг контролирует позицию; при достижении защитного уровня позиция закрывается по рынку, при этом брокер может исполнить стоп-лосс или тейк-профит автономно.

Дополнительные замечания

  • Для корректной работы стратегии необходимо подписаться на свечи и поток Level1.
  • Если данные об инструменте (шаг цены или стоимость шага) недоступны, алгоритм объёма автоматически возвращается к фиксированному Volume.
  • Трейлинг реализован через рыночное закрытие, что упрощает перенос стратегии между разными торговыми площадками и симуляторами.
namespace StockSharp.Samples.Strategies;

using System;

using Ecng.Common;

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

/// <summary>
/// Fast In Fast Out scalping strategy: quick entry/exit using EMA and RSI.
/// </summary>
public class FxfFastInFastOutStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _rsiPeriod;

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

	public int EmaPeriod
	{
		get => _emaPeriod.Value;
		set => _emaPeriod.Value = value;
	}

	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

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

		_emaPeriod = Param(nameof(EmaPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA period", "Indicators");

		_rsiPeriod = Param(nameof(RsiPeriod), 7)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period", "Indicators");
	}

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

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		decimal? prevRsi = null;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, rsi, (candle, emaVal, rsiVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				var close = candle.ClosePrice;

				if (prevRsi.HasValue)
				{
					if (prevRsi.Value <= 50 && rsiVal > 50 && close > emaVal && Position <= 0)
						BuyMarket();
					else if (prevRsi.Value >= 50 && rsiVal < 50 && close < emaVal && Position >= 0)
						SellMarket();
				}

				prevRsi = rsiVal;
			})
			.Start();

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