Exp Leading 策略
该策略基于自定义 Leading 指标的交叉系统,该指标源自 John F. Ehlers 的《Cybernetics Analysis for Stock and Futures》。指标包含两条曲线:
- NetLead – 由参数
Alpha1和Alpha2控制的平滑领先滤波器。 - EMA – 采用固定系数 0.5 的指数移动平均。
策略在选定周期的已完成K线运行。当 NetLead 下穿 EMA 时,预期行情向上反转并开多单;当 NetLead 上穿 EMA 时,开空单。若已有持仓,会在发送反向订单时自动平仓。
参数
Alpha1– 领先计算的系数,默认0.25。Alpha2– 应用于领先结果的平滑因子,默认0.33。CandleType– 用于计算的K线类型,默认四小时周期。StopLoss– 绝对价格单位的止损,默认1000。TakeProfit– 绝对价格单位的止盈,默认2000。
交易逻辑
- 每根完成的K线都会更新 NetLead 和 EMA 数值。
- 若上一根K线 NetLead 高于 EMA,而最新一根 NetLead 低于 EMA,则发送买入市价单。
- 若上一根 NetLead 低于 EMA,而最新一根高于 EMA,则发送卖出市价单。
- 通过
StartProtection自动应用止损和止盈。
该示例用于演示如何将 MetaTrader 策略迁移到 StockSharp 的高级 API。
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()