Стратегия Exp Leading
Эта стратегия реализует систему пересечений на основе кастомного индикатора Leading, описанного Джоном Эллерсом в книге «Cybernetics Analysis for Stock and Futures». Индикатор строит две линии:
- NetLead – сглаженный ведущий фильтр, управляемый коэффициентами
Alpha1иAlpha2. - EMA – экспоненциальное среднее с постоянным множителем 0.5.
Стратегия работает на завершённых свечах выбранного таймфрейма. Когда линия NetLead пересекает снизу линию EMA, предполагается разворот вверх и открывается длинная позиция. При пересечении NetLead сверху EMA открывается короткая позиция. Предыдущая позиция закрывается автоматически встречным ордером.
Параметры
Alpha1– коэффициент промежуточного расчёта. По умолчанию0.25.Alpha2– фактор сглаживания. По умолчанию0.33.CandleType– тип свечей для расчётов. По умолчанию четырёхчасовой таймфрейм.StopLoss– стоп‑лосс в абсолютных единицах цены. По умолчанию1000.TakeProfit– тейк‑профит в абсолютных единицах цены. По умолчанию2000.
Логика торговли
- На каждой завершённой свече пересчитываются значения NetLead и EMA.
- Если на предыдущей свече NetLead была выше EMA, а на последней стала ниже, отправляется рыночный ордер на покупку.
- Если NetLead была ниже EMA, а затем стала выше, отправляется рыночный ордер на продажу.
- Метод
StartProtectionавтоматически применяет стоп‑лосс и тейк‑профит.
Пример предназначен для демонстрации переноса стратегии из MetaTrader на высокоуровневый API StockSharp.
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;
using StockSharp.Algo;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on crossover of custom Leading indicator and its EMA.
/// Opens long position when NetLead crosses below EMA and short when crosses above.
/// </summary>
public class ExpLeadingStrategy : Strategy
{
private readonly StrategyParam<decimal> _alpha1;
private readonly StrategyParam<decimal> _alpha2;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<int> _cooldownBars;
private bool _isInitialized;
private bool _hasPrev2;
private decimal _pricePrev;
private decimal _leadPrev;
private decimal _netLeadPrev;
private decimal _emaPrev;
private decimal _prevNetLead;
private decimal _prevEma;
private decimal _prev2NetLead;
private decimal _prev2Ema;
private int _barsSinceTrade;
/// <summary>
/// Alpha1 coefficient for Leading indicator.
/// </summary>
public decimal Alpha1 { get => _alpha1.Value; set => _alpha1.Value = value; }
/// <summary>
/// Alpha2 coefficient for Leading indicator.
/// </summary>
public decimal Alpha2 { get => _alpha2.Value; set => _alpha2.Value = value; }
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Stop loss in price units.
/// </summary>
public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
/// <summary>
/// Take profit in price units.
/// </summary>
public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
/// <summary>
/// Bars to wait after a completed trade.
/// </summary>
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
/// <summary>
/// Initialize strategy parameters.
/// </summary>
public ExpLeadingStrategy()
{
_alpha1 = Param(nameof(Alpha1), 0.25m)
.SetDisplay("Alpha1", "Alpha1 coefficient", "Indicator");
_alpha2 = Param(nameof(Alpha2), 0.33m)
.SetDisplay("Alpha2", "Alpha2 coefficient", "Indicator");
_cooldownBars = Param(nameof(CooldownBars), 1)
.SetDisplay("Cooldown Bars", "Bars to wait after a completed trade", "Protection");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle data type", "General");
_stopLoss = Param(nameof(StopLoss), 1000m)
.SetDisplay("Stop Loss", "Stop loss in price", "Protection");
_takeProfit = Param(nameof(TakeProfit), 2000m)
.SetDisplay("Take Profit", "Take profit in price", "Protection");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_isInitialized = false;
_hasPrev2 = false;
_pricePrev = 0m;
_leadPrev = 0m;
_netLeadPrev = 0m;
_emaPrev = 0m;
_prevNetLead = 0m;
_prevEma = 0m;
_prev2NetLead = 0m;
_prev2Ema = 0m;
_barsSinceTrade = CooldownBars;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(new Unit(TakeProfit, UnitTypes.Absolute), new Unit(StopLoss, UnitTypes.Absolute));
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_barsSinceTrade < CooldownBars)
_barsSinceTrade++;
var price = (candle.HighPrice + candle.LowPrice) / 2m;
if (!_isInitialized)
{
_pricePrev = price;
_leadPrev = price;
_netLeadPrev = price;
_emaPrev = price;
_prevNetLead = price;
_prevEma = price;
_isInitialized = true;
return;
}
var lead = 2m * price + (Alpha1 - 2m) * _pricePrev + (1m - Alpha1) * _leadPrev;
var netLead = Alpha2 * lead + (1m - Alpha2) * _netLeadPrev;
var ema = 0.5m * price + 0.5m * _emaPrev;
if (_hasPrev2)
{
var buySignal = _prev2NetLead > _prev2Ema && _prevNetLead < _prevEma;
var sellSignal = _prev2NetLead < _prev2Ema && _prevNetLead > _prevEma;
if (_barsSinceTrade >= CooldownBars)
{
if (buySignal && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_barsSinceTrade = 0;
}
else if (sellSignal && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_barsSinceTrade = 0;
}
}
}
else
{
_hasPrev2 = true;
}
_prev2NetLead = _prevNetLead;
_prev2Ema = _prevEma;
_prevNetLead = netLead;
_prevEma = ema;
_pricePrev = price;
_leadPrev = lead;
_netLeadPrev = netLead;
_emaPrev = ema;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Strategies import Strategy
class exp_leading_strategy(Strategy):
def __init__(self):
super(exp_leading_strategy, self).__init__()
self._alpha1 = self.Param("Alpha1", 0.25) \
.SetDisplay("Alpha1", "Alpha1 coefficient", "Indicator")
self._alpha2 = self.Param("Alpha2", 0.33) \
.SetDisplay("Alpha2", "Alpha2 coefficient", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle data type", "General")
self._stop_loss = self.Param("StopLoss", 1000.0) \
.SetDisplay("Stop Loss", "Stop loss in price", "Protection")
self._take_profit = self.Param("TakeProfit", 2000.0) \
.SetDisplay("Take Profit", "Take profit in price", "Protection")
self._cooldown_bars = self.Param("CooldownBars", 1) \
.SetDisplay("Cooldown Bars", "Bars to wait after a completed trade", "Protection")
self._is_initialized = False
self._has_prev2 = False
self._price_prev = 0.0
self._lead_prev = 0.0
self._net_lead_prev = 0.0
self._ema_prev = 0.0
self._prev_net_lead = 0.0
self._prev_ema = 0.0
self._prev2_net_lead = 0.0
self._prev2_ema = 0.0
self._bars_since_trade = 0
@property
def Alpha1(self):
return self._alpha1.Value
@Alpha1.setter
def Alpha1(self, value):
self._alpha1.Value = value
@property
def Alpha2(self):
return self._alpha2.Value
@Alpha2.setter
def Alpha2(self, value):
self._alpha2.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def CooldownBars(self):
return self._cooldown_bars.Value
@CooldownBars.setter
def CooldownBars(self, value):
self._cooldown_bars.Value = value
def OnStarted2(self, time):
super(exp_leading_strategy, self).OnStarted2(time)
self.StartProtection(
Unit(self.TakeProfit, UnitTypes.Absolute),
Unit(self.StopLoss, UnitTypes.Absolute))
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._bars_since_trade < self.CooldownBars:
self._bars_since_trade += 1
price = (float(candle.HighPrice) + float(candle.LowPrice)) / 2.0
a1 = float(self.Alpha1)
a2 = float(self.Alpha2)
if not self._is_initialized:
self._price_prev = price
self._lead_prev = price
self._net_lead_prev = price
self._ema_prev = price
self._prev_net_lead = price
self._prev_ema = price
self._is_initialized = True
return
lead = 2.0 * price + (a1 - 2.0) * self._price_prev + (1.0 - a1) * self._lead_prev
net_lead = a2 * lead + (1.0 - a2) * self._net_lead_prev
ema = 0.5 * price + 0.5 * self._ema_prev
if self._has_prev2:
buy_signal = self._prev2_net_lead > self._prev2_ema and self._prev_net_lead < self._prev_ema
sell_signal = self._prev2_net_lead < self._prev2_ema and self._prev_net_lead > self._prev_ema
if self._bars_since_trade >= self.CooldownBars:
pos = self.Position
if buy_signal and pos <= 0:
self.BuyMarket(self.Volume + abs(pos))
self._bars_since_trade = 0
elif sell_signal and pos >= 0:
self.SellMarket(self.Volume + abs(pos))
self._bars_since_trade = 0
else:
self._has_prev2 = True
self._prev2_net_lead = self._prev_net_lead
self._prev2_ema = self._prev_ema
self._prev_net_lead = net_lead
self._prev_ema = ema
self._price_prev = price
self._lead_prev = lead
self._net_lead_prev = net_lead
self._ema_prev = ema
def OnReseted(self):
super(exp_leading_strategy, self).OnReseted()
self._is_initialized = False
self._has_prev2 = False
self._price_prev = 0.0
self._lead_prev = 0.0
self._net_lead_prev = 0.0
self._ema_prev = 0.0
self._prev_net_lead = 0.0
self._prev_ema = 0.0
self._prev2_net_lead = 0.0
self._prev2_ema = 0.0
self._bars_since_trade = self.CooldownBars
def CreateClone(self):
return exp_leading_strategy()