DLMv FX Fish Grid Strategy переносит логику одноимённого советника из MetaTrader. В основе лежит осциллятор «FX Fish 2MA»: на выбранную цену применяется преобразование Фишера, результат сглаживается скользящей средней и при пересечении индикатора с его средней на нужной стороне от нулевой линии генерируются торговые сигналы. Управление позициями повторяет «сеточный» подход оригинала — дополнительные входы выполняются с заданным интервалом, при необходимости автоматически выставляются лимитные ордера, а риск контролируется защитными параметрами.
Алгоритм торговли
Расчёт индикатора
Максимум и минимум за CalculatePeriod свечей формируют рабочий диапазон.
К цене, определяемой параметром AppliedPrice, применяется преобразование Фишера с коэффициентом сглаживания 0.67, как в исходном MQL-скрипте.
Простая скользящая средняя (MaPeriod) от значения Фишера используется как базовая линия.
Условия входа
Покупка: две последовательные величины Фишера ниже нуля, текущая пересекает скользящую среднюю снизу вверх.
Продажа: две последовательные величины Фишера выше нуля, текущая пересекает среднюю сверху вниз.
При включении ReverseSignals условия зеркально инвертируются.
Выставление ордеров
По сигналу стратегия при необходимости закрывает противоположные позиции (CloseOpposite).
Количество добавочных входов ограничивается MaxTrades, причём каждый новый вход допускается только при соблюдении дистанции DistancePips от последней сделки.
Опция SetLimitOrders автоматически размещает лимитные ордера на том же расстоянии, формируя «сетку» как в оригинальном советнике.
Защита позиций
Параметры 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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class dlmv_fx_fish_grid_strategy(Strategy):
def __init__(self):
super(dlmv_fx_fish_grid_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._period = self.Param("Period", 14) \
.SetDisplay("Period", "Channel period", "Indicators")
self._prev_high = None
self._prev_low = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def Period(self):
return self._period.Value
def OnReseted(self):
super(dlmv_fx_fish_grid_strategy, self).OnReseted()
self._prev_high = None
self._prev_low = None
def OnStarted2(self, time):
super(dlmv_fx_fish_grid_strategy, self).OnStarted2(time)
self._prev_high = None
self._prev_low = None
highest = Highest()
highest.Length = self.Period
lowest = Lowest()
lowest.Length = self.Period
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(highest, lowest, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, highest)
self.DrawIndicator(area, lowest)
self.DrawOwnTrades(area)
def _on_process(self, candle, high_value, low_value):
if candle.State != CandleStates.Finished:
return
hv = float(high_value)
lv = float(low_value)
close = float(candle.ClosePrice)
if self._prev_high is None or self._prev_low is None:
self._prev_high = hv
self._prev_low = lv
return
if close > self._prev_high and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif close < self._prev_low and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_high = hv
self._prev_low = lv
def CreateClone(self):
return dlmv_fx_fish_grid_strategy()