Открыть на GitHub

DLMv FX Fish Grid Strategy

Обзор

DLMv FX Fish Grid Strategy переносит логику одноимённого советника из MetaTrader. В основе лежит осциллятор «FX Fish 2MA»: на выбранную цену применяется преобразование Фишера, результат сглаживается скользящей средней и при пересечении индикатора с его средней на нужной стороне от нулевой линии генерируются торговые сигналы. Управление позициями повторяет «сеточный» подход оригинала — дополнительные входы выполняются с заданным интервалом, при необходимости автоматически выставляются лимитные ордера, а риск контролируется защитными параметрами.

Алгоритм торговли

  1. Расчёт индикатора
    • Максимум и минимум за CalculatePeriod свечей формируют рабочий диапазон.
    • К цене, определяемой параметром AppliedPrice, применяется преобразование Фишера с коэффициентом сглаживания 0.67, как в исходном MQL-скрипте.
    • Простая скользящая средняя (MaPeriod) от значения Фишера используется как базовая линия.
  2. Условия входа
    • Покупка: две последовательные величины Фишера ниже нуля, текущая пересекает скользящую среднюю снизу вверх.
    • Продажа: две последовательные величины Фишера выше нуля, текущая пересекает среднюю сверху вниз.
    • При включении ReverseSignals условия зеркально инвертируются.
  3. Выставление ордеров
    • По сигналу стратегия при необходимости закрывает противоположные позиции (CloseOpposite).
    • Количество добавочных входов ограничивается MaxTrades, причём каждый новый вход допускается только при соблюдении дистанции DistancePips от последней сделки.
    • Опция SetLimitOrders автоматически размещает лимитные ордера на том же расстоянии, формируя «сетку» как в оригинальном советнике.
  4. Защита позиций
    • Параметры StopLossPips, TakeProfitPips, TrailingStopPips и TrailingStepPips переводятся в абсолютные значения через StartProtection и устанавливают стоп-лосс, тейк-профит и трейлинг.
    • TimeLiveSeconds задаёт максимальное время удержания позиции; по истечении лимита все ордера закрываются и снимаются.
    • Если TradeOnFriday отключён, то в пятницу стратегия прекращает торговлю и закрывает остаточные позиции.

Параметры

Параметр Описание
OrderVolume Объём каждой сделки (в лотах).
StopLossPips Расстояние до стоп-лосса в пунктах (0 — без стопа).
TakeProfitPips Расстояние до тейк-профита в пунктах (0 — без тейка).
TrailingStopPips Дистанция трейлинг-стопа (0 — отключён).
TrailingStepPips Шаг подтяжки трейлинг-стопа.
MaxTrades Максимальное количество одновременных сделок в одном направлении (0 — без ограничения).
DistancePips Минимальная дистанция между входами и шаг для лимитных ордеров сетки.
TradeOnFriday Разрешение торговли по пятницам. При значении false позиции закрываются в день пятницы.
TimeLiveSeconds Максимальное время жизни позиции в секундах.
ReverseSignals Инверсия логики входа.
SetLimitOrders Автоматическое размещение лимитных ордеров на заданном расстоянии.
CloseOpposite Закрытие встречных позиций перед открытием новой сделки.
CalculatePeriod Длина окна для расчёта диапазона Фишера.
MaPeriod Период скользящей средней по значению Фишера.
AppliedPrice Тип цены для преобразования (close, open, high, low, median, typical, weighted).
CandleType Тип/таймфрейм свечей, которые использует стратегия.

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

  • Преобразование пунктов в абсолютные значения выполняется как Security.PriceStep * 10, что соответствует обработке пятизначных котировок в MQL-версии.
  • Все лимитные ордера автоматически отменяются при смене сигнала, запрете торговли в пятницу или срабатывании тайм-аута TimeLiveSeconds.
  • Для определения пересечений стратегия хранит предыдущие значения Фишера и его скользящей средней, избегая дорогостоящих запросов к истории индикатора.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// DLMv FX Fish Grid strategy. Uses Highest/Lowest range with Fisher transform crossover.
/// </summary>
public class DlmvFxFishGridStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _period;

	private decimal? _prevFish;
	private decimal _prevValue;

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

	public int Period
	{
		get => _period.Value;
		set => _period.Value = value;
	}

	public DlmvFxFishGridStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_period = Param(nameof(Period), 20)
			.SetGreaterThanZero()
			.SetDisplay("Period", "Lookback period for high/low range", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFish = null;
		_prevValue = 0m;
	}

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

		_prevFish = null;
		_prevValue = 0m;

		var highest = new Highest { Length = Period };
		var lowest = new Lowest { Length = Period };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(highest, lowest, ProcessCandle)
			.Start();

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

	private void ProcessCandle(ICandleMessage candle, decimal high, decimal low)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var range = high - low;
		var midPrice = (candle.HighPrice + candle.LowPrice) / 2m;

		var normalized = range != 0m ? (midPrice - low) / range : 0.5m;
		var value = 0.66m * (normalized - 0.5m) + 0.67m * _prevValue;
		value = Math.Min(Math.Max(value, -0.999m), 0.999m);

		var ratio = (double)((1m + value) / (1m - value));
		var fish = 0.5m * (decimal)Math.Log(ratio);

		_prevValue = value;

		if (_prevFish == null)
		{
			_prevFish = fish;
			return;
		}

		// Fisher crosses zero from below → buy
		if (_prevFish.Value < 0m && fish >= 0m && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Fisher crosses zero from above → sell
		else if (_prevFish.Value > 0m && fish <= 0m && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevFish = fish;
	}
}