Exp CyclePeriod Strategy
该策略使用 CyclePeriod 指标来识别市场周期的转折。当指标上升时开多仓,当指标下降时开空仓,并在指标反向时平掉相反持仓。
详情
- 入场条件:
- 多头:CyclePeriod 上升且当前值高于前一值。
- 空头:CyclePeriod 下降且当前值低于前一值。
- 多/空方向:多头和空头。
- 出场条件:
- 当 CyclePeriod 向上转折时平掉空单。
- 当 CyclePeriod 向下转折时平掉多单。
- 止损止盈:使用以价格单位表示的止盈和止损。
- 默认参数:
CandleType= TimeSpan.FromHours(6).TimeFrame().Alpha= 0.07.SignalBar= 1.TakeProfit= 2000.StopLoss= 1000.BuyPosOpen= true.SellPosOpen= true.BuyPosClose= true。SellPosClose= true。
- 过滤器:
- 类别:趋势跟随
- 方向:多头和空头
- 指标:CyclePeriod
- 止损:是
- 复杂度:中等
- 时间框架:6 小时
- 季节性:否
- 神经网络:否
- 背离:否
- 风险等级:中等
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>
/// Strategy based on Ehlers CyclePeriod concept using EMA crossover as proxy.
/// Detects short-term cycles via fast/slow EMA and trades reversals.
/// </summary>
public class ExpCyclePeriodStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<bool> _buyPosOpen;
private readonly StrategyParam<bool> _sellPosOpen;
private decimal _prevFast;
private decimal _prevSlow;
private bool _prevReady;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
public bool BuyPosOpen { get => _buyPosOpen.Value; set => _buyPosOpen.Value = value; }
public bool SellPosOpen { get => _sellPosOpen.Value; set => _sellPosOpen.Value = value; }
public ExpCyclePeriodStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(6).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_fastPeriod = Param(nameof(FastPeriod), 7)
.SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 21)
.SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_takeProfit = Param(nameof(TakeProfit), 2000m)
.SetDisplay("Take Profit", "Take profit in price", "Risk");
_stopLoss = Param(nameof(StopLoss), 1000m)
.SetDisplay("Stop Loss", "Stop loss in price", "Risk");
_buyPosOpen = Param(nameof(BuyPosOpen), true)
.SetDisplay("Buy Open", "Allow long entries", "Logic");
_sellPosOpen = Param(nameof(SellPosOpen), true)
.SetDisplay("Sell Open", "Allow short entries", "Logic");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0m;
_prevSlow = 0m;
_prevReady = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(new Unit(TakeProfit, UnitTypes.Absolute), new Unit(StopLoss, UnitTypes.Absolute));
var fastEma = new ExponentialMovingAverage { Length = FastPeriod };
var slowEma = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_prevReady)
{
_prevFast = fastValue;
_prevSlow = slowValue;
_prevReady = true;
return;
}
// Crossover detection
var prevDiff = _prevFast - _prevSlow;
var currDiff = fastValue - slowValue;
// Golden cross: fast crosses above slow
if (prevDiff <= 0 && currDiff > 0)
{
if (BuyPosOpen && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
}
// Death cross: fast crosses below slow
else if (prevDiff >= 0 && currDiff < 0)
{
if (SellPosOpen && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
}
_prevFast = fastValue;
_prevSlow = slowValue;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class exp_cycle_period_strategy(Strategy):
def __init__(self):
super(exp_cycle_period_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(6))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._fast_period = self.Param("FastPeriod", 7) \
.SetDisplay("Fast Period", "Fast EMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 21) \
.SetDisplay("Slow Period", "Slow EMA period", "Indicator")
self._take_profit = self.Param("TakeProfit", 2000.0) \
.SetDisplay("Take Profit", "Take profit in price", "Risk")
self._stop_loss = self.Param("StopLoss", 1000.0) \
.SetDisplay("Stop Loss", "Stop loss in price", "Risk")
self._buy_pos_open = self.Param("BuyPosOpen", True) \
.SetDisplay("Buy Open", "Allow long entries", "Logic")
self._sell_pos_open = self.Param("SellPosOpen", True) \
.SetDisplay("Sell Open", "Allow short entries", "Logic")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._prev_ready = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def FastPeriod(self):
return self._fast_period.Value
@FastPeriod.setter
def FastPeriod(self, value):
self._fast_period.Value = value
@property
def SlowPeriod(self):
return self._slow_period.Value
@SlowPeriod.setter
def SlowPeriod(self, value):
self._slow_period.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def BuyPosOpen(self):
return self._buy_pos_open.Value
@BuyPosOpen.setter
def BuyPosOpen(self, value):
self._buy_pos_open.Value = value
@property
def SellPosOpen(self):
return self._sell_pos_open.Value
@SellPosOpen.setter
def SellPosOpen(self, value):
self._sell_pos_open.Value = value
def OnStarted2(self, time):
super(exp_cycle_period_strategy, self).OnStarted2(time)
self.StartProtection(
stopLoss=Unit(self.StopLoss, UnitTypes.Absolute),
takeProfit=Unit(self.TakeProfit, UnitTypes.Absolute)
)
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.FastPeriod
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.SlowPeriod
self.SubscribeCandles(self.CandleType) \
.Bind(fast_ema, slow_ema, self.ProcessCandle) \
.Start()
def ProcessCandle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._prev_ready:
self._prev_fast = fast_val
self._prev_slow = slow_val
self._prev_ready = True
return
prev_diff = self._prev_fast - self._prev_slow
curr_diff = fast_val - slow_val
if prev_diff <= 0 and curr_diff > 0:
if self.BuyPosOpen and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif prev_diff >= 0 and curr_diff < 0:
if self.SellPosOpen and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_fast = fast_val
self._prev_slow = slow_val
def OnReseted(self):
super(exp_cycle_period_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._prev_ready = False
def CreateClone(self):
return exp_cycle_period_strategy()