Exp CyclePeriod Strategy
This strategy uses the CyclePeriod indicator to detect market cycle turns. It opens long positions when the indicator rises and short positions when it falls, closing opposite positions accordingly.
Details
- Entry Criteria:
- Long: CyclePeriod is rising and the current value is above the previous one.
- Short: CyclePeriod is falling and the current value is below the previous one.
- Long/Short: Long and Short.
- Exit Criteria:
- Close short when CyclePeriod turns upward.
- Close long when CyclePeriod turns downward.
- Stops: Uses take profit and stop loss in price units.
- Default Values:
CandleType= TimeSpan.FromHours(6).TimeFrame().Alpha= 0.07.SignalBar= 1.TakeProfit= 2000.StopLoss= 1000.BuyPosOpen= true.SellPosOpen= true.BuyPosClose= true.SellPosClose= true.
- Filters:
- Category: Trend following
- Direction: Long & Short
- Indicators: CyclePeriod
- Stops: Yes
- Complexity: Intermediate
- Timeframe: 6-hour
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: Medium
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()