Стратегия Ichimoku Clouds Long and Short
Эта стратегия использует пересечение Tenkan-sen и Kijun-sen индикатора Ichimoku. Пересечения классифицируются как сильные, нейтральные или слабые в зависимости от положения Tenkan относительно облака. В выбранном торговом режиме открываются длинные или короткие позиции, когда появляется сигнал нужной силы. Дополнительно можно задать проценты тейк-профита и стоп-лосса для закрытия позиции или использовать противоположный сигнал.
Подробности
- Условия входа:
- Tenkan-sen пересекает Kijun-sen вверх и сила сигнала соответствует выбранным опциям для лонга.
- Tenkan-sen пересекает Kijun-sen вниз и сила сигнала соответствует выбранным опциям для шорта.
- Направление: Настраивается, по умолчанию лонг.
- Условия выхода:
- Противоположные сигналы согласно заданным настройкам.
- Процентный тейк-профит или стоп-лосс.
- Стопы: Процентные тейк-профит и стоп-лосс.
- Параметры по умолчанию:
TenkanPeriod= 9KijunPeriod= 26SenkouSpanPeriod= 52TakeProfitPct= 0StopLossPct= 0
- Фильтры:
- Категория: Тренд
- Направление: Лонг и шорт
- Индикаторы: Ichimoku
- Стопы: Опционально
- Сложность: Средняя
- Таймфрейм: Любой
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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 Clouds strategy allowing long or short modes with customizable signal strength filters and optional take-profit / stop-loss levels.
/// Entry is triggered by Tenkan-sen crossing Kijun-sen and classified as strong, neutral or weak depending on the cloud position.
/// </summary>
public class IchimokuCloudsLongAndShortStrategy : Strategy
{
private readonly StrategyParam<int> _tenkanPeriod;
private readonly StrategyParam<int> _kijunPeriod;
private readonly StrategyParam<int> _senkouSpanPeriod;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<string> _tradeDirection;
private readonly StrategyParam<string> _entrySignalOptionsLong;
private readonly StrategyParam<string> _exitSignalOptionsLong;
private readonly StrategyParam<string> _entrySignalOptionsShort;
private readonly StrategyParam<string> _exitSignalOptionsShort;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevTenkan;
private decimal _entryPrice;
private decimal _prevKijun;
/// <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 SenkouSpanPeriod
{
get => _senkouSpanPeriod.Value;
set => _senkouSpanPeriod.Value = value;
}
/// <summary>
/// Take profit percentage.
/// </summary>
public decimal TakeProfitPct
{
get => _takeProfitPct.Value;
set => _takeProfitPct.Value = value;
}
/// <summary>
/// Stop loss percentage.
/// </summary>
public decimal StopLossPct
{
get => _stopLossPct.Value;
set => _stopLossPct.Value = value;
}
/// <summary>
/// Trading mode (Long or Short).
/// </summary>
public string TradeDirection
{
get => _tradeDirection.Value;
set => _tradeDirection.Value = value;
}
/// <summary>
/// Entry signal options when trading long.
/// </summary>
public string EntrySignalOptionsLong
{
get => _entrySignalOptionsLong.Value;
set => _entrySignalOptionsLong.Value = value;
}
/// <summary>
/// Exit signal options when trading long.
/// </summary>
public string ExitSignalOptionsLong
{
get => _exitSignalOptionsLong.Value;
set => _exitSignalOptionsLong.Value = value;
}
/// <summary>
/// Entry signal options when trading short.
/// </summary>
public string EntrySignalOptionsShort
{
get => _entrySignalOptionsShort.Value;
set => _entrySignalOptionsShort.Value = value;
}
/// <summary>
/// Exit signal options when trading short.
/// </summary>
public string ExitSignalOptionsShort
{
get => _exitSignalOptionsShort.Value;
set => _exitSignalOptionsShort.Value = value;
}
/// <summary>
/// Candle type used for the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize Ichimoku Clouds Long/Short strategy.
/// </summary>
public IchimokuCloudsLongAndShortStrategy()
{
_tenkanPeriod = Param(nameof(TenkanPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Tenkan Period", "Tenkan-sen period", "Indicators");
_kijunPeriod = Param(nameof(KijunPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Kijun Period", "Kijun-sen period", "Indicators");
_senkouSpanPeriod = Param(nameof(SenkouSpanPeriod), 52)
.SetGreaterThanZero()
.SetDisplay("Senkou Span Period", "Senkou Span B period", "Indicators");
_takeProfitPct = Param(nameof(TakeProfitPct), 0m)
.SetDisplay("Take Profit %", "Take profit percentage (0 - disabled)", "Risk Management");
_stopLossPct = Param(nameof(StopLossPct), 0m)
.SetDisplay("Stop Loss %", "Stop loss percentage (0 - disabled)", "Risk Management");
_tradeDirection = Param(nameof(TradeDirection), "Long")
.SetDisplay("Trading Mode", "Trade direction: Long or Short", "General");
_entrySignalOptionsLong = Param(nameof(EntrySignalOptionsLong), "Bullish All")
.SetDisplay("Entry Signal (Long)", "Entry signal filter for long mode", "Long Mode Signals");
_exitSignalOptionsLong = Param(nameof(ExitSignalOptionsLong), "Bearish Weak")
.SetDisplay("Exit Signal (Long)", "Exit signal filter for long mode", "Long Mode Signals");
_entrySignalOptionsShort = Param(nameof(EntrySignalOptionsShort), "None")
.SetDisplay("Entry Signal (Short)", "Entry signal filter for short mode", "Short Mode Signals");
_exitSignalOptionsShort = Param(nameof(ExitSignalOptionsShort), "None")
.SetDisplay("Exit Signal (Short)", "Exit signal filter for short mode", "Short Mode Signals");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Candle type for the strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevTenkan = 0;
_prevKijun = 0;
_entryPrice = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ichimoku = new Ichimoku
{
Tenkan = { Length = TenkanPeriod },
Kijun = { Length = KijunPeriod },
SenkouB = { Length = SenkouSpanPeriod }
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(ichimoku, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private enum SignalStrengths
{
Strong,
Neutral,
Weak
}
private static bool IsSignalAllowed(string option, SignalStrengths strength, bool bullish)
{
if (option == "None")
return false;
if (bullish)
{
return option switch
{
"Bullish Strong" => strength == SignalStrengths.Strong,
"Bullish Neutral" => strength == SignalStrengths.Neutral,
"Bullish Weak" => strength == SignalStrengths.Weak,
"Bullish Strong and Neutral" => strength is SignalStrengths.Strong or SignalStrengths.Neutral,
"Bullish Neutral and Weak" => strength is SignalStrengths.Neutral or SignalStrengths.Weak,
"Bullish Strong and Weak" => strength is SignalStrengths.Strong or SignalStrengths.Weak,
"Bullish All" => true,
_ => false
};
}
return option switch
{
"Bearish Strong" => strength == SignalStrengths.Strong,
"Bearish Neutral" => strength == SignalStrengths.Neutral,
"Bearish Weak" => strength == SignalStrengths.Weak,
"Bearish Strong and Neutral" => strength is SignalStrengths.Strong or SignalStrengths.Neutral,
"Bearish Neutral and Weak" => strength is SignalStrengths.Neutral or SignalStrengths.Weak,
"Bearish Strong and Weak" => strength is SignalStrengths.Strong or SignalStrengths.Weak,
"Bearish All" => true,
_ => false
};
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue ichimokuValue)
{
if (candle.State != CandleStates.Finished)
return;
if (ichimokuValue is not IchimokuValue ichimoku)
return;
if (ichimoku.Tenkan is not decimal tenkan)
return;
if (ichimoku.Kijun is not decimal kijun)
return;
if (ichimoku.SenkouA is not decimal senkouA)
return;
if (ichimoku.SenkouB is not decimal senkouB)
return;
var upperCloud = Math.Max(senkouA, senkouB);
var lowerCloud = Math.Min(senkouA, senkouB);
var crossUp = tenkan > kijun && _prevTenkan <= _prevKijun;
var crossDown = tenkan < kijun && _prevTenkan >= _prevKijun;
if (crossUp)
{
var strength = tenkan > upperCloud ? SignalStrengths.Strong : tenkan < lowerCloud ? SignalStrengths.Weak : SignalStrengths.Neutral;
if (TradeDirection == "Long" && IsSignalAllowed(EntrySignalOptionsLong, strength, true) && Position <= 0)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
}
else if (TradeDirection == "Short" && IsSignalAllowed(ExitSignalOptionsShort, strength, true) && Position < 0)
{
BuyMarket();
}
}
else if (crossDown)
{
var strength = tenkan < lowerCloud ? SignalStrengths.Strong : tenkan > upperCloud ? SignalStrengths.Weak : SignalStrengths.Neutral;
if (TradeDirection == "Short" && IsSignalAllowed(EntrySignalOptionsShort, strength, false) && Position >= 0)
{
SellMarket();
_entryPrice = candle.ClosePrice;
}
else if (TradeDirection == "Long" && IsSignalAllowed(ExitSignalOptionsLong, strength, false) && Position > 0)
{
SellMarket();
}
}
if (Position > 0)
{
if (TakeProfitPct > 0 && candle.ClosePrice >= _entryPrice * (1 + TakeProfitPct / 100m))
SellMarket();
else if (StopLossPct > 0 && candle.ClosePrice <= _entryPrice * (1 - StopLossPct / 100m))
SellMarket();
}
else if (Position < 0)
{
if (TakeProfitPct > 0 && candle.ClosePrice <= _entryPrice * (1 - TakeProfitPct / 100m))
BuyMarket();
else if (StopLossPct > 0 && candle.ClosePrice >= _entryPrice * (1 + StopLossPct / 100m))
BuyMarket();
}
_prevTenkan = tenkan;
_prevKijun = kijun;
}
}
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
from StockSharp.Algo.Indicators import Ichimoku
from StockSharp.Algo.Strategies import Strategy
class ichimoku_clouds_long_and_short_strategy(Strategy):
def __init__(self):
super(ichimoku_clouds_long_and_short_strategy, self).__init__()
self._tenkan_period = self.Param("TenkanPeriod", 9) \
.SetGreaterThanZero() \
.SetDisplay("Tenkan Period", "Tenkan-sen period", "Indicators")
self._kijun_period = self.Param("KijunPeriod", 26) \
.SetGreaterThanZero() \
.SetDisplay("Kijun Period", "Kijun-sen period", "Indicators")
self._senkou_span_period = self.Param("SenkouSpanPeriod", 52) \
.SetGreaterThanZero() \
.SetDisplay("Senkou Span Period", "Senkou Span B period", "Indicators")
self._take_profit_pct = self.Param("TakeProfitPct", 0.0) \
.SetDisplay("Take Profit %", "Take profit percentage (0 - disabled)", "Risk Management")
self._stop_loss_pct = self.Param("StopLossPct", 0.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage (0 - disabled)", "Risk Management")
self._trade_direction = self.Param("TradeDirection", "Long") \
.SetDisplay("Trading Mode", "Trade direction: Long or Short", "General")
self._entry_signal_options_long = self.Param("EntrySignalOptionsLong", "Bullish All") \
.SetDisplay("Entry Signal (Long)", "Entry signal filter for long mode", "Long Mode Signals")
self._exit_signal_options_long = self.Param("ExitSignalOptionsLong", "Bearish Weak") \
.SetDisplay("Exit Signal (Long)", "Exit signal filter for long mode", "Long Mode Signals")
self._entry_signal_options_short = self.Param("EntrySignalOptionsShort", "None") \
.SetDisplay("Entry Signal (Short)", "Entry signal filter for short mode", "Short Mode Signals")
self._exit_signal_options_short = self.Param("ExitSignalOptionsShort", "None") \
.SetDisplay("Exit Signal (Short)", "Exit signal filter for short mode", "Short Mode Signals")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Candle type for the strategy", "General")
self._prev_tenkan = 0.0
self._prev_kijun = 0.0
self._entry_price = 0.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_clouds_long_and_short_strategy, self).OnReseted()
self._prev_tenkan = 0.0
self._prev_kijun = 0.0
self._entry_price = 0.0
def OnStarted2(self, time):
super(ichimoku_clouds_long_and_short_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_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(self._ichimoku, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _is_signal_allowed(self, option, strength, bullish):
if option == "None":
return False
if bullish:
if option == "Bullish Strong":
return strength == "Strong"
elif option == "Bullish Neutral":
return strength == "Neutral"
elif option == "Bullish Weak":
return strength == "Weak"
elif option == "Bullish Strong and Neutral":
return strength in ("Strong", "Neutral")
elif option == "Bullish Neutral and Weak":
return strength in ("Neutral", "Weak")
elif option == "Bullish Strong and Weak":
return strength in ("Strong", "Weak")
elif option == "Bullish All":
return True
return False
else:
if option == "Bearish Strong":
return strength == "Strong"
elif option == "Bearish Neutral":
return strength == "Neutral"
elif option == "Bearish Weak":
return strength == "Weak"
elif option == "Bearish Strong and Neutral":
return strength in ("Strong", "Neutral")
elif option == "Bearish Neutral and Weak":
return strength in ("Neutral", "Weak")
elif option == "Bearish Strong and Weak":
return strength in ("Strong", "Weak")
elif option == "Bearish All":
return True
return False
def OnProcess(self, candle, ichimoku_val):
if candle.State != CandleStates.Finished:
return
tenkan_v = ichimoku_val.Tenkan
kijun_v = ichimoku_val.Kijun
senkou_a = ichimoku_val.SenkouA
senkou_b = ichimoku_val.SenkouB
if tenkan_v is None or kijun_v is None or senkou_a is None or senkou_b is None:
return
tenkan = float(tenkan_v)
kijun = float(kijun_v)
sa = float(senkou_a)
sb = float(senkou_b)
upper_cloud = max(sa, sb)
lower_cloud = min(sa, sb)
close = float(candle.ClosePrice)
direction = str(self._trade_direction.Value)
cross_up = tenkan > kijun and self._prev_tenkan <= self._prev_kijun
cross_down = tenkan < kijun and self._prev_tenkan >= self._prev_kijun
if cross_up:
if tenkan > upper_cloud:
strength = "Strong"
elif tenkan < lower_cloud:
strength = "Weak"
else:
strength = "Neutral"
if direction == "Long" and self._is_signal_allowed(str(self._entry_signal_options_long.Value), strength, True) and self.Position <= 0:
self.BuyMarket()
self._entry_price = close
elif direction == "Short" and self._is_signal_allowed(str(self._exit_signal_options_short.Value), strength, True) and self.Position < 0:
self.BuyMarket()
elif cross_down:
if tenkan < lower_cloud:
strength = "Strong"
elif tenkan > upper_cloud:
strength = "Weak"
else:
strength = "Neutral"
if direction == "Short" and self._is_signal_allowed(str(self._entry_signal_options_short.Value), strength, False) and self.Position >= 0:
self.SellMarket()
self._entry_price = close
elif direction == "Long" and self._is_signal_allowed(str(self._exit_signal_options_long.Value), strength, False) and self.Position > 0:
self.SellMarket()
tp_pct = float(self._take_profit_pct.Value)
sl_pct = float(self._stop_loss_pct.Value)
if self.Position > 0 and self._entry_price > 0:
if tp_pct > 0 and close >= self._entry_price * (1.0 + tp_pct / 100.0):
self.SellMarket()
elif sl_pct > 0 and close <= self._entry_price * (1.0 - sl_pct / 100.0):
self.SellMarket()
elif self.Position < 0 and self._entry_price > 0:
if tp_pct > 0 and close <= self._entry_price * (1.0 - tp_pct / 100.0):
self.BuyMarket()
elif sl_pct > 0 and close >= self._entry_price * (1.0 + sl_pct / 100.0):
self.BuyMarket()
self._prev_tenkan = tenkan
self._prev_kijun = kijun
def CreateClone(self):
return ichimoku_clouds_long_and_short_strategy()