Estrategia Exp Leading
Esta estrategia implementa un sistema de cruce basado en el indicador personalizado Leading descrito por John F. Ehlers en Cybernetics Analysis for Stock and Futures. El indicador calcula dos líneas:
- NetLead – filtro líder suavizado controlado por los coeficientes
Alpha1 y Alpha2.
- EMA – una media móvil exponencial simple con un factor constante de 0.5.
La estrategia opera sobre velas cerradas del marco temporal seleccionado. Cuando la línea NetLead cruza por debajo de la línea EMA, se anticipa una reversión alcista y se abre una posición larga. Por el contrario, cuando NetLead cruza por encima de la línea EMA, se abre una posición corta. La posición anterior, si existe, se cierra implícitamente cuando se envía una orden opuesta.
Parámetros
Alpha1 – coeficiente para el cálculo intermedio del líder. Predeterminado: 0.25.
Alpha2 – factor de suavizado aplicado al resultado líder. Predeterminado: 0.33.
CandleType – tipo de datos de vela usado para los cálculos. Predeterminado: marco temporal de 4 horas.
StopLoss – stop loss en unidades absolutas de precio. Predeterminado: 1000.
TakeProfit – take profit en unidades absolutas de precio. Predeterminado: 2000.
Lógica de Trading
- Cada vela cerrada actualiza los valores de NetLead y EMA.
- Si la barra anterior mostró NetLead por encima de EMA y la última barra muestra NetLead por debajo de EMA, se envía una orden de mercado de compra.
- Si la barra anterior mostró NetLead por debajo de EMA y la última barra muestra NetLead por encima de EMA, se envía una orden de mercado de venta.
- Se utiliza
StartProtection para aplicar automáticamente las reglas de stop-loss y take-profit.
Este ejemplo está destinado a fines educativos para demostrar cómo una estrategia de MetaTrader puede portarse a la API de alto nivel de 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()