LeMan趋势策略
LeMan趋势策略通过最近的高点和低点来计算多头和空头压力。它测量当前K线与三个不同周期的最高价和最低价之间的距离,并使用指数移动平均线(EMA)对这些距离进行平滑,从而形成多头线和空头线。当两条线交叉时,可能预示着趋势变化。
当多头线从下方穿越空头线时,策略开多或平掉已有的空单。相反,当空头线位于多头线上方时,策略开空或平多。该方法不使用额外过滤器,仅依赖于最近高低点的相对强度。
细节
- 入场条件
- 做多:多头线向上穿越空头线。
- 做空:空头线向上穿越多头线。
- 多空方向:支持双向交易。
- 出场条件
- 反向交叉信号关闭当前仓位。
- 止损:默认无。
- 默认参数
Min= 13Midle= 21Max= 34EMA周期= 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()