Стратегия LeMan Trend
Стратегия LeMan Trend вычисляет бычье и медвежье давление на основе последних максимумов и минимумов. Она измеряет расстояние между текущей свечой и тремя наборами экстремумов с разными периодами, после чего сглаживает полученные значения экспоненциальным скользящим средним (EMA). Это формирует две линии — быков и медведей. Пересечение этих линий указывает на возможное изменение тенденции.
Когда линия быков пересекает линию медведей снизу вверх, стратегия открывает длинную позицию или закрывает существующую короткую. Если линия медведей оказывается выше линии быков, открывается короткая позиция или закрывается длинная. Дополнительные фильтры не используются — упор делается только на относительную силу последних максимумов и минимумов.
Детали
- Условия входа
- Длинная: линия быков пересекает линию медведей снизу вверх.
- Короткая: линия медведей пересекает линию быков снизу вверх.
- Длинные/короткие: поддерживаются оба направления.
- Условия выхода
- Противоположное пересечение закрывает активную позицию.
- Стопы: по умолчанию отсутствуют.
- Значения по умолчанию
Min= 13Midle= 21Max= 34Период EMA= 3Таймфрейм= 4 часа
- Фильтры
- Категория: Следование тренду
- Направление: Оба
- Индикаторы: Highest, Lowest, EMA
- Стопы: Нет
- Сложность: Средняя
- Таймфрейм: Среднесрочный
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Умеренный
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>
/// Leman Trend strategy using high/low differences smoothed by EMA.
/// Opens long when bullish pressure exceeds bearish, short otherwise.
/// </summary>
public class LeManTrendStrategy : Strategy
{
private readonly StrategyParam<int> _min;
private readonly StrategyParam<int> _midle;
private readonly StrategyParam<int> _max;
private readonly StrategyParam<int> _periodEma;
private readonly StrategyParam<DataType> _candleType;
private Highest _highMin;
private Highest _highMidle;
private Highest _highMax;
private Lowest _lowMin;
private Lowest _lowMidle;
private Lowest _lowMax;
private ExponentialMovingAverage _bullsEma;
private ExponentialMovingAverage _bearsEma;
private decimal _prevBulls;
private decimal _prevBears;
public int Min
{
get => _min.Value;
set => _min.Value = value;
}
public int Midle
{
get => _midle.Value;
set => _midle.Value = value;
}
public int Max
{
get => _max.Value;
set => _max.Value = value;
}
public int PeriodEma
{
get => _periodEma.Value;
set => _periodEma.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public LeManTrendStrategy()
{
_min = Param(nameof(Min), 13)
.SetGreaterThanZero()
.SetDisplay("Min Period", "Minimum lookback for highs/lows", "Indicator")
.SetOptimize(5, 25, 1);
_midle = Param(nameof(Midle), 21)
.SetGreaterThanZero()
.SetDisplay("Middle Period", "Middle lookback for highs/lows", "Indicator")
.SetOptimize(10, 40, 1);
_max = Param(nameof(Max), 34)
.SetGreaterThanZero()
.SetDisplay("Max Period", "Maximum lookback for highs/lows", "Indicator")
.SetOptimize(20, 60, 1);
_periodEma = Param(nameof(PeriodEma), 3)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "Smoothing period for bulls/bears", "Indicator")
.SetOptimize(2, 10, 1);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Time frame for calculations", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnReseted()
{
base.OnReseted();
_highMin = default;
_highMidle = default;
_highMax = default;
_lowMin = default;
_lowMidle = default;
_lowMax = default;
_bullsEma = default;
_bearsEma = default;
_prevBulls = default;
_prevBears = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_highMin = new Highest { Length = Min };
_highMidle = new Highest { Length = Midle };
_highMax = new Highest { Length = Max };
_lowMin = new Lowest { Length = Min };
_lowMidle = new Lowest { Length = Midle };
_lowMax = new Lowest { Length = Max };
_bullsEma = new ExponentialMovingAverage { Length = PeriodEma };
_bearsEma = new ExponentialMovingAverage { Length = PeriodEma };
Indicators.Add(_highMin);
Indicators.Add(_highMidle);
Indicators.Add(_highMax);
Indicators.Add(_lowMin);
Indicators.Add(_lowMidle);
Indicators.Add(_lowMax);
Indicators.Add(_bullsEma);
Indicators.Add(_bearsEma);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var closeInput = new DecimalIndicatorValue(_highMin, candle.ClosePrice, candle.OpenTime) { IsFinal = true };
var highMinVal = _highMin.Process(closeInput).ToDecimal();
var highMidleVal = _highMidle.Process(new DecimalIndicatorValue(_highMidle, candle.ClosePrice, candle.OpenTime) { IsFinal = true }).ToDecimal();
var highMaxVal = _highMax.Process(new DecimalIndicatorValue(_highMax, candle.ClosePrice, candle.OpenTime) { IsFinal = true }).ToDecimal();
var lowMinVal = _lowMin.Process(new DecimalIndicatorValue(_lowMin, candle.ClosePrice, candle.OpenTime) { IsFinal = true }).ToDecimal();
var lowMidleVal = _lowMidle.Process(new DecimalIndicatorValue(_lowMidle, candle.ClosePrice, candle.OpenTime) { IsFinal = true }).ToDecimal();
var lowMaxVal = _lowMax.Process(new DecimalIndicatorValue(_lowMax, candle.ClosePrice, candle.OpenTime) { IsFinal = true }).ToDecimal();
if (!_highMax.IsFormed || !_lowMax.IsFormed)
return;
var hh = (candle.HighPrice - highMinVal) + (candle.HighPrice - highMidleVal) + (candle.HighPrice - highMaxVal);
var ll = (lowMinVal - candle.LowPrice) + (lowMidleVal - candle.LowPrice) + (lowMaxVal - candle.LowPrice);
var bullsVal = _bullsEma.Process(new DecimalIndicatorValue(_bullsEma, hh, candle.OpenTime) { IsFinal = true }).ToDecimal();
var bearsVal = _bearsEma.Process(new DecimalIndicatorValue(_bearsEma, ll, candle.OpenTime) { IsFinal = true }).ToDecimal();
if (!_bullsEma.IsFormed || !_bearsEma.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_prevBulls <= _prevBears && bullsVal > bearsVal && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (_prevBulls >= _prevBears && bullsVal < bearsVal && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevBulls = bullsVal;
_prevBears = bearsVal;
}
}
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, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class le_man_trend_strategy(Strategy):
def __init__(self):
super(le_man_trend_strategy, self).__init__()
self._min = self.Param("Min", 13) \
.SetDisplay("Min Period", "Minimum lookback for highs/lows", "Indicator")
self._midle = self.Param("Midle", 21) \
.SetDisplay("Middle Period", "Middle lookback for highs/lows", "Indicator")
self._max = self.Param("Max", 34) \
.SetDisplay("Max Period", "Maximum lookback for highs/lows", "Indicator")
self._period_ema = self.Param("PeriodEma", 3) \
.SetDisplay("EMA Period", "Smoothing period for bulls/bears", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Time frame for calculations", "General")
self._high_min = None
self._high_midle = None
self._high_max = None
self._low_min = None
self._low_midle = None
self._low_max = None
self._bulls_ema = None
self._bears_ema = None
self._prev_bulls = 0.0
self._prev_bears = 0.0
@property
def min_period(self):
return self._min.Value
@property
def midle_period(self):
return self._midle.Value
@property
def max_period(self):
return self._max.Value
@property
def period_ema(self):
return self._period_ema.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(le_man_trend_strategy, self).OnReseted()
self._high_min = None
self._high_midle = None
self._high_max = None
self._low_min = None
self._low_midle = None
self._low_max = None
self._bulls_ema = None
self._bears_ema = None
self._prev_bulls = 0.0
self._prev_bears = 0.0
def OnStarted2(self, time):
super(le_man_trend_strategy, self).OnStarted2(time)
self._high_min = Highest()
self._high_min.Length = self.min_period
self._high_midle = Highest()
self._high_midle.Length = self.midle_period
self._high_max = Highest()
self._high_max.Length = self.max_period
self._low_min = Lowest()
self._low_min.Length = self.min_period
self._low_midle = Lowest()
self._low_midle.Length = self.midle_period
self._low_max = Lowest()
self._low_max.Length = self.max_period
self._bulls_ema = ExponentialMovingAverage()
self._bulls_ema.Length = self.period_ema
self._bears_ema = ExponentialMovingAverage()
self._bears_ema.Length = self.period_ema
self.Indicators.Add(self._high_min)
self.Indicators.Add(self._high_midle)
self.Indicators.Add(self._high_max)
self.Indicators.Add(self._low_min)
self.Indicators.Add(self._low_midle)
self.Indicators.Add(self._low_max)
self.Indicators.Add(self._bulls_ema)
self.Indicators.Add(self._bears_ema)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
close_val = candle.ClosePrice
t = candle.OpenTime
high_min_val = float(process_float(self._high_min, close_val, t, True))
high_midle_val = float(process_float(self._high_midle, close_val, t, True))
high_max_val = float(process_float(self._high_max, close_val, t, True))
low_min_val = float(process_float(self._low_min, close_val, t, True))
low_midle_val = float(process_float(self._low_midle, close_val, t, True))
low_max_val = float(process_float(self._low_max, close_val, t, True))
if not self._high_max.IsFormed or not self._low_max.IsFormed:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
hh = (high - high_min_val) + (high - high_midle_val) + (high - high_max_val)
ll = (low_min_val - low) + (low_midle_val - low) + (low_max_val - low)
bulls_val = float(process_float(self._bulls_ema, hh, t, True))
bears_val = float(process_float(self._bears_ema, ll, t, True))
if not self._bulls_ema.IsFormed or not self._bears_ema.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._prev_bulls <= self._prev_bears and bulls_val > bears_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev_bulls >= self._prev_bears and bulls_val < bears_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_bulls = bulls_val
self._prev_bears = bears_val
def CreateClone(self):
return le_man_trend_strategy()