Стратегия Fisher Transform X2
Стратегия использует индикатор Fisher Transform на двух таймфреймах. Старший таймфрейм определяет общий тренд, а младший генерирует входы, когда Fisher пересекает своё предыдущее значение против тренда. Дополнительные параметры позволяют закрывать позиции при смене тренда или по сигналам пересечения.
Детали
- Условия входа:
- Long:
Трендовый Fisher растёт&&Сигнальный Fisher пересекает вниз своё предыдущее значение - Short:
Трендовый Fisher падает&&Сигнальный Fisher пересекает вверх своё предыдущее значение
- Long:
- Длинные/Короткие: Оба направления
- Условия выхода:
- Опционально при смене тренда
- Опционально при обратном пересечении Fisher на сигнальном таймфрейме
- Стопы: Тейк-профит и стоп-лосс в пунктах
- Значения по умолчанию:
Trend Length= 10Signal Length= 10Trend Timeframe= 6 часовSignal Timeframe= 30 минутTake Profit= 2000 пунктовStop Loss= 1000 пунктов
- Фильтры:
- Категория: Следование тренду
- Направление: Оба
- Индикаторы: Fisher Transform
- Стопы: Да
- Сложность: Средняя
- Таймфрейм: Мульти-таймфрейм
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Fisher Transform strategy using two Fisher indicators (trend + signal).
/// Trend Fisher defines direction; Signal Fisher generates entries.
/// Both use same timeframe but different lengths.
/// </summary>
public class FisherTransformX2Strategy : Strategy
{
private readonly StrategyParam<int> _trendLength;
private readonly StrategyParam<int> _signalLength;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<DataType> _candleType;
private EhlersFisherTransform _trendFisher;
private EhlersFisherTransform _signalFisher;
private decimal _prevTrend;
private decimal _prevSignal;
private decimal _prevPrevSignal;
private int _trendDirection;
private int _count;
public int TrendLength { get => _trendLength.Value; set => _trendLength.Value = value; }
public int SignalLength { get => _signalLength.Value; set => _signalLength.Value = value; }
public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public FisherTransformX2Strategy()
{
_trendLength = Param(nameof(TrendLength), 40)
.SetGreaterThanZero()
.SetDisplay("Trend Length", "Fisher length for trend", "Parameters")
.SetOptimize(10, 30, 2);
_signalLength = Param(nameof(SignalLength), 20)
.SetGreaterThanZero()
.SetDisplay("Signal Length", "Fisher length for signal", "Parameters")
.SetOptimize(5, 20, 1);
_takeProfit = Param(nameof(TakeProfit), 2000m)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Take profit in price units", "Risk");
_stopLoss = Param(nameof(StopLoss), 1000m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevTrend = 0m;
_prevSignal = 0m;
_prevPrevSignal = 0m;
_trendDirection = 0;
_count = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_trendFisher = new EhlersFisherTransform { Length = TrendLength };
_signalFisher = new EhlersFisherTransform { Length = SignalLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_signalFisher, ProcessCandle)
.Start();
StartProtection(
new Unit(TakeProfit, UnitTypes.Absolute),
new Unit(StopLoss, UnitTypes.Absolute));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _signalFisher);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue signalResult)
{
if (candle.State != CandleStates.Finished)
return;
// Process trend Fisher manually with the candle
var trendResult = _trendFisher.Process(candle);
if (!_trendFisher.IsFormed || !_signalFisher.IsFormed)
return;
var signalVal = ((IEhlersFisherTransformValue)signalResult).MainLine ?? 0m;
var trendVal = ((IEhlersFisherTransformValue)trendResult).MainLine ?? 0m;
_count++;
if (_count < 3)
{
_prevPrevSignal = _prevSignal;
_prevSignal = signalVal;
_prevTrend = trendVal;
return;
}
// Update trend direction
if (trendVal > _prevTrend)
_trendDirection = 1;
else if (trendVal < _prevTrend)
_trendDirection = -1;
// Signal crossover
var signalCrossUp = signalVal > _prevSignal && _prevSignal <= _prevPrevSignal && signalVal < 0m;
var signalCrossDown = signalVal < _prevSignal && _prevSignal >= _prevPrevSignal && signalVal > 0m;
if (_trendDirection > 0 && signalCrossUp && Position <= 0)
BuyMarket();
else if (_trendDirection < 0 && signalCrossDown && Position >= 0)
SellMarket();
_prevTrend = trendVal;
_prevPrevSignal = _prevSignal;
_prevSignal = signalVal;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import EhlersFisherTransform, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class fisher_transform_x2_strategy(Strategy):
"""
Fisher Transform X2: Dual Fisher indicator strategy.
Trend Fisher defines direction; Signal Fisher generates entries.
Uses StartProtection for SL/TP.
"""
def __init__(self):
super(fisher_transform_x2_strategy, self).__init__()
self._trend_length = self.Param("TrendLength", 40) \
.SetDisplay("Trend Length", "Fisher length for trend", "Parameters")
self._signal_length = self.Param("SignalLength", 20) \
.SetDisplay("Signal Length", "Fisher length for signal", "Parameters")
self._take_profit = self.Param("TakeProfit", 2000.0) \
.SetDisplay("Take Profit", "Take profit in price units", "Risk")
self._stop_loss = self.Param("StopLoss", 1000.0) \
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._trend_fisher = None
self._prev_trend = 0.0
self._prev_signal = 0.0
self._prev_prev_signal = 0.0
self._trend_direction = 0
self._count = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(fisher_transform_x2_strategy, self).OnReseted()
self._prev_trend = 0.0
self._prev_signal = 0.0
self._prev_prev_signal = 0.0
self._trend_direction = 0
self._count = 0
def OnStarted2(self, time):
super(fisher_transform_x2_strategy, self).OnStarted2(time)
self._trend_fisher = EhlersFisherTransform()
self._trend_fisher.Length = self._trend_length.Value
self.Indicators.Add(self._trend_fisher)
signal_fisher = EhlersFisherTransform()
signal_fisher.Length = self._signal_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(signal_fisher, self._process_candle).Start()
tp = self._take_profit.Value
sl = self._stop_loss.Value
self.StartProtection(
Unit(tp, UnitTypes.Absolute),
Unit(sl, UnitTypes.Absolute))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, signal_fisher)
self.DrawOwnTrades(area)
def _process_candle(self, candle, signal_result):
if candle.State != CandleStates.Finished:
return
trend_input = CandleIndicatorValue(self._trend_fisher, candle)
trend_input.IsFinal = True
trend_result = self._trend_fisher.Process(trend_input)
if not self._trend_fisher.IsFormed:
return
signal_main = signal_result.MainLine
trend_main = trend_result.MainLine
signal_val = float(signal_main) if signal_main is not None else 0.0
trend_val = float(trend_main) if trend_main is not None else 0.0
self._count += 1
if self._count < 3:
self._prev_prev_signal = self._prev_signal
self._prev_signal = signal_val
self._prev_trend = trend_val
return
if trend_val > self._prev_trend:
self._trend_direction = 1
elif trend_val < self._prev_trend:
self._trend_direction = -1
signal_cross_up = signal_val > self._prev_signal and self._prev_signal <= self._prev_prev_signal and signal_val < 0
signal_cross_down = signal_val < self._prev_signal and self._prev_signal >= self._prev_prev_signal and signal_val > 0
if self._trend_direction > 0 and signal_cross_up and self.Position <= 0:
self.BuyMarket()
elif self._trend_direction < 0 and signal_cross_down and self.Position >= 0:
self.SellMarket()
self._prev_trend = trend_val
self._prev_prev_signal = self._prev_signal
self._prev_signal = signal_val
def CreateClone(self):
return fisher_transform_x2_strategy()