Ichimoku Cloud Buy & Sell
Стратегия использует облако Ишимоку с фильтрами EMA и объёма. Покупка происходит, когда цена выше облака и объём повышен, а продажа — когда цена ниже облака. Позиции закрываются при пересечении цены с EMA в противоположном направлении.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Ichimoku Cloud strategy with EMA and volume filters.
/// Buys above the cloud with strong volume and sells below the cloud.
/// </summary>
public class IchimokuCloudBuySellStrategy : Strategy
{
private readonly StrategyParam<int> _tenkanPeriod;
private readonly StrategyParam<int> _kijunPeriod;
private readonly StrategyParam<int> _senkouSpanBPeriod;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _avgVolumeLength;
private readonly StrategyParam<bool> _useStopLoss;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<bool> _requireAboveEma;
private readonly StrategyParam<bool> _requireBelowEma;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevAvgVolume;
private int _cooldown;
/// <summary>
/// Tenkan-sen period.
/// </summary>
public int TenkanPeriod
{
get => _tenkanPeriod.Value;
set => _tenkanPeriod.Value = value;
}
/// <summary>
/// Kijun-sen period.
/// </summary>
public int KijunPeriod
{
get => _kijunPeriod.Value;
set => _kijunPeriod.Value = value;
}
/// <summary>
/// Senkou Span B period.
/// </summary>
public int SenkouSpanBPeriod
{
get => _senkouSpanBPeriod.Value;
set => _senkouSpanBPeriod.Value = value;
}
/// <summary>
/// EMA length for exit filter.
/// </summary>
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
/// <summary>
/// Length for average volume calculation.
/// </summary>
public int AvgVolumeLength
{
get => _avgVolumeLength.Value;
set => _avgVolumeLength.Value = value;
}
/// <summary>
/// Enable stop-loss protection.
/// </summary>
public bool UseStopLoss
{
get => _useStopLoss.Value;
set => _useStopLoss.Value = value;
}
/// <summary>
/// Stop-loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Require price above EMA for long entry.
/// </summary>
public bool RequireAboveEma
{
get => _requireAboveEma.Value;
set => _requireAboveEma.Value = value;
}
/// <summary>
/// Require price below EMA for short entry.
/// </summary>
public bool RequireBelowEma
{
get => _requireBelowEma.Value;
set => _requireBelowEma.Value = value;
}
/// <summary>
/// Candle type used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize strategy parameters.
/// </summary>
public IchimokuCloudBuySellStrategy()
{
_tenkanPeriod = Param(nameof(TenkanPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Tenkan Period", "Tenkan-sen periods", "Ichimoku Settings")
.SetOptimize(7, 11, 1);
_kijunPeriod = Param(nameof(KijunPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Kijun Period", "Kijun-sen periods", "Ichimoku Settings")
.SetOptimize(20, 30, 2);
_senkouSpanBPeriod = Param(nameof(SenkouSpanBPeriod), 52)
.SetGreaterThanZero()
.SetDisplay("Senkou Span B Period", "Senkou Span B periods", "Ichimoku Settings")
.SetOptimize(40, 60, 4);
_emaPeriod = Param(nameof(EmaPeriod), 44)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA length for exit", "Filters")
.SetOptimize(20, 80, 5);
_avgVolumeLength = Param(nameof(AvgVolumeLength), 10)
.SetGreaterThanZero()
.SetDisplay("Average Volume Length", "Length for average volume filter", "Filters")
.SetOptimize(5, 20, 5);
_useStopLoss = Param(nameof(UseStopLoss), true)
.SetDisplay("Use Stop Loss", "Enable stop-loss exits", "Risk Management");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss %", "Stop-loss percentage", "Risk Management")
.SetOptimize(1m, 5m, 0.5m);
_requireAboveEma = Param(nameof(RequireAboveEma), true)
.SetDisplay("Only Buy Above EMA", "Require price above EMA for long entries", "Filters");
_requireBelowEma = Param(nameof(RequireBelowEma), true)
.SetDisplay("Only Sell Below EMA", "Require price below EMA for short entries", "Filters");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevAvgVolume = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ichimoku = new Ichimoku
{
Tenkan = { Length = TenkanPeriod },
Kijun = { Length = KijunPeriod },
SenkouB = { Length = SenkouSpanBPeriod }
};
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var volumeMa = new SimpleMovingAverage { Length = AvgVolumeLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(ichimoku, ema, volumeMa, ProcessCandle)
.Start();
if (UseStopLoss)
StartProtection(takeProfit: default, stopLoss: new Unit(StopLossPercent, UnitTypes.Percent));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue ichimokuValue, IIndicatorValue emaValue, IIndicatorValue volumeValue)
{
if (candle.State != CandleStates.Finished)
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
if (emaValue.IsEmpty || volumeValue.IsEmpty || ichimokuValue.IsEmpty)
return;
var ema = emaValue.ToDecimal();
var avgVol = volumeValue.ToDecimal();
_prevAvgVolume = avgVol;
if (ichimokuValue is not IchimokuValue ichi)
return;
if (ichi.SenkouA is not decimal senkouA ||
ichi.SenkouB is not decimal senkouB)
return;
var upperKumo = Math.Max(senkouA, senkouB);
var lowerKumo = Math.Min(senkouA, senkouB);
var buyCondition = candle.ClosePrice > upperKumo && (!RequireAboveEma || candle.ClosePrice > ema);
var sellCondition = candle.ClosePrice < lowerKumo && (!RequireBelowEma || candle.ClosePrice < ema);
if (buyCondition && Position <= 0)
{
BuyMarket();
_cooldown = 5;
}
else if (sellCondition && Position >= 0)
{
SellMarket();
_cooldown = 5;
}
else if (Position > 0 && candle.ClosePrice < ema)
{
SellMarket();
_cooldown = 5;
}
else if (Position < 0 && candle.ClosePrice > ema)
{
BuyMarket();
_cooldown = 5;
}
}
}
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 Ichimoku, ExponentialMovingAverage, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ichimoku_cloud_buy_sell_strategy(Strategy):
def __init__(self):
super(ichimoku_cloud_buy_sell_strategy, self).__init__()
self._tenkan_period = self.Param("TenkanPeriod", 9) \
.SetGreaterThanZero() \
.SetDisplay("Tenkan Period", "Tenkan-sen periods", "Ichimoku Settings")
self._kijun_period = self.Param("KijunPeriod", 26) \
.SetGreaterThanZero() \
.SetDisplay("Kijun Period", "Kijun-sen periods", "Ichimoku Settings")
self._senkou_span_b_period = self.Param("SenkouSpanBPeriod", 52) \
.SetGreaterThanZero() \
.SetDisplay("Senkou Span B Period", "Senkou Span B periods", "Ichimoku Settings")
self._ema_period = self.Param("EmaPeriod", 44) \
.SetGreaterThanZero() \
.SetDisplay("EMA Period", "EMA length for exit", "Filters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(ichimoku_cloud_buy_sell_strategy, self).OnReseted()
self._cooldown = 0
def OnStarted2(self, time):
super(ichimoku_cloud_buy_sell_strategy, self).OnStarted2(time)
self._ichimoku = Ichimoku()
self._ichimoku.Tenkan.Length = self._tenkan_period.Value
self._ichimoku.Kijun.Length = self._kijun_period.Value
self._ichimoku.SenkouB.Length = self._senkou_span_b_period.Value
self._ema = ExponentialMovingAverage()
self._ema.Length = self._ema_period.Value
volume_ma = SimpleMovingAverage()
volume_ma.Length = 10
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(self._ichimoku, self._ema, volume_ma, self.OnProcess).Start()
self.StartProtection(None, Unit(2, UnitTypes.Percent))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._ema)
self.DrawOwnTrades(area)
def OnProcess(self, candle, ichimoku_val, ema_val, volume_val):
if candle.State != CandleStates.Finished:
return
if self._cooldown > 0:
self._cooldown -= 1
return
if ema_val.IsEmpty or ichimoku_val.IsEmpty:
return
ema_v = float(ema_val)
senkou_a = ichimoku_val.SenkouA
senkou_b = ichimoku_val.SenkouB
if senkou_a is None or senkou_b is None:
return
senkou_a_v = float(senkou_a)
senkou_b_v = float(senkou_b)
upper_kumo = max(senkou_a_v, senkou_b_v)
lower_kumo = min(senkou_a_v, senkou_b_v)
close = float(candle.ClosePrice)
buy_cond = close > upper_kumo and close > ema_v
sell_cond = close < lower_kumo and close < ema_v
if buy_cond and self.Position <= 0:
self.BuyMarket()
self._cooldown = 5
elif sell_cond and self.Position >= 0:
self.SellMarket()
self._cooldown = 5
elif self.Position > 0 and close < ema_v:
self.SellMarket()
self._cooldown = 5
elif self.Position < 0 and close > ema_v:
self.BuyMarket()
self._cooldown = 5
def CreateClone(self):
return ichimoku_cloud_buy_sell_strategy()