Стратегия Ichimoku Oscillator
Стратегия Ichimoku Oscillator использует осциллятор, построенный на базе индикатора Ichimoku. Осциллятор рассчитывается как разница между линией запаздывания и Senkou Span B за вычетом разницы между Tenkan-sen и Kijun-sen. Полученное значение сглаживается средним Юрика (Jurik moving average).
Вход в позицию происходит, когда сглаженный осциллятор меняет направление и пересекает своё предыдущее значение, что позволяет попытаться поймать начало тренда.
Принципы работы
- Покупка: осциллятор растёт и текущее значение пересекает предыдущее сверху. Перед входом закрывается короткая позиция.
- Продажа: осциллятор падает и текущее значение пересекает предыдущее снизу. Перед входом закрывается длинная позиция.
- Для управления рисками можно задать стоп‑лосс и тейк‑профит в процентах.
Параметры
- Tenkan Period – период линии Tenkan-sen индикатора Ichimoku.
- Kijun Period – период линии Kijun-sen.
- Senkou Span B Period – период Senkou Span B.
- Smoothing Period – период сглаживания осциллятора средним Юрика.
- Candle Type – таймфрейм для расчётов.
- Stop Loss % – размер стоп‑лосса в процентах.
- Enable Stop Loss – включение или отключение стоп‑лосса.
- Take Profit % – размер тейк‑профита в процентах.
Индикаторы
- Ichimoku
- Jurik Moving Average
Примечания
Стратегия предоставляется в образовательных целях. Перед реальной торговлей рекомендуется протестировать её на исторических данных.
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>
/// Strategy based on Ichimoku oscillator smoothed by EMA.
/// Opens long when oscillator turns up, short when it turns down.
/// </summary>
public class IchimokuOscillatorStrategy : Strategy
{
private readonly StrategyParam<int> _tenkanPeriod;
private readonly StrategyParam<int> _kijunPeriod;
private readonly StrategyParam<int> _senkouSpanBPeriod;
private readonly StrategyParam<int> _smoothingPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<decimal> _takeProfitPercent;
private ExponentialMovingAverage _ema;
private decimal? _prevValue;
private decimal? _prevPrevValue;
public int TenkanPeriod { get => _tenkanPeriod.Value; set => _tenkanPeriod.Value = value; }
public int KijunPeriod { get => _kijunPeriod.Value; set => _kijunPeriod.Value = value; }
public int SenkouSpanBPeriod { get => _senkouSpanBPeriod.Value; set => _senkouSpanBPeriod.Value = value; }
public int SmoothingPeriod { get => _smoothingPeriod.Value; set => _smoothingPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public decimal StopLossPercent { get => _stopLossPercent.Value; set => _stopLossPercent.Value = value; }
public decimal TakeProfitPercent { get => _takeProfitPercent.Value; set => _takeProfitPercent.Value = value; }
public IchimokuOscillatorStrategy()
{
_tenkanPeriod = Param(nameof(TenkanPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Tenkan Period", "Period for Tenkan-sen line", "Ichimoku");
_kijunPeriod = Param(nameof(KijunPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Kijun Period", "Period for Kijun-sen line", "Ichimoku");
_senkouSpanBPeriod = Param(nameof(SenkouSpanBPeriod), 52)
.SetGreaterThanZero()
.SetDisplay("Senkou Span B Period", "Period for Senkou Span B", "Ichimoku");
_smoothingPeriod = Param(nameof(SmoothingPeriod), 7)
.SetGreaterThanZero()
.SetDisplay("Smoothing Period", "Period for smoothing EMA", "Oscillator");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for calculation", "Main");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetDisplay("Stop Loss %", "Stop loss in percent", "Risk");
_takeProfitPercent = Param(nameof(TakeProfitPercent), 4m)
.SetDisplay("Take Profit %", "Take profit in percent", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ema = default;
_prevValue = null;
_prevPrevValue = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ichimoku = new Ichimoku();
ichimoku.Tenkan.Length = TenkanPeriod;
ichimoku.Kijun.Length = KijunPeriod;
ichimoku.SenkouB.Length = SenkouSpanBPeriod;
_ema = new ExponentialMovingAverage { Length = SmoothingPeriod };
Indicators.Add(_ema);
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(ichimoku, ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(TakeProfitPercent, UnitTypes.Percent),
stopLoss: new Unit(StopLossPercent, UnitTypes.Percent),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue ichimokuValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!ichimokuValue.IsFormed)
return;
var ich = (IchimokuValue)ichimokuValue;
if (ich.Chinkou is not decimal chikou ||
ich.SenkouB is not decimal spanB ||
ich.Tenkan is not decimal tenkan ||
ich.Kijun is not decimal kijun)
return;
var osc = (chikou - spanB) - (tenkan - kijun);
var emaVal = _ema.Process(osc, candle.OpenTime, true);
if (!emaVal.IsFormed)
return;
var current = emaVal.ToDecimal();
if (_prevValue is decimal prev && _prevPrevValue is decimal prevPrev)
{
var rising = prev > prevPrev;
var falling = prev < prevPrev;
if (rising && current >= prev && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (falling && current <= prev && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
}
_prevPrevValue = _prevValue;
_prevValue = current;
}
}
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, Ichimoku
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class ichimoku_oscillator_strategy(Strategy):
def __init__(self):
super(ichimoku_oscillator_strategy, self).__init__()
self._tenkan_period = self.Param("TenkanPeriod", 9) \
.SetDisplay("Tenkan Period", "Period for Tenkan-sen line", "Ichimoku")
self._kijun_period = self.Param("KijunPeriod", 26) \
.SetDisplay("Kijun Period", "Period for Kijun-sen line", "Ichimoku")
self._senkou_span_b_period = self.Param("SenkouSpanBPeriod", 52) \
.SetDisplay("Senkou Span B Period", "Period for Senkou Span B", "Ichimoku")
self._smoothing_period = self.Param("SmoothingPeriod", 7) \
.SetDisplay("Smoothing Period", "Period for smoothing EMA", "Oscillator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for calculation", "Main")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss in percent", "Risk")
self._take_profit_percent = self.Param("TakeProfitPercent", 4.0) \
.SetDisplay("Take Profit %", "Take profit in percent", "Risk")
self._ema = None
self._prev_value = None
self._prev_prev_value = None
@property
def TenkanPeriod(self):
return self._tenkan_period.Value
@TenkanPeriod.setter
def TenkanPeriod(self, value):
self._tenkan_period.Value = value
@property
def KijunPeriod(self):
return self._kijun_period.Value
@KijunPeriod.setter
def KijunPeriod(self, value):
self._kijun_period.Value = value
@property
def SenkouSpanBPeriod(self):
return self._senkou_span_b_period.Value
@SenkouSpanBPeriod.setter
def SenkouSpanBPeriod(self, value):
self._senkou_span_b_period.Value = value
@property
def SmoothingPeriod(self):
return self._smoothing_period.Value
@SmoothingPeriod.setter
def SmoothingPeriod(self, value):
self._smoothing_period.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def StopLossPercent(self):
return self._stop_loss_percent.Value
@StopLossPercent.setter
def StopLossPercent(self, value):
self._stop_loss_percent.Value = value
@property
def TakeProfitPercent(self):
return self._take_profit_percent.Value
@TakeProfitPercent.setter
def TakeProfitPercent(self, value):
self._take_profit_percent.Value = value
def OnStarted2(self, time):
super(ichimoku_oscillator_strategy, self).OnStarted2(time)
ichimoku = Ichimoku()
ichimoku.Tenkan.Length = self.TenkanPeriod
ichimoku.Kijun.Length = self.KijunPeriod
ichimoku.SenkouB.Length = self.SenkouSpanBPeriod
self._ema = ExponentialMovingAverage()
self._ema.Length = self.SmoothingPeriod
self.Indicators.Add(self._ema)
self._prev_value = None
self._prev_prev_value = None
self.SubscribeCandles(self.CandleType) \
.BindEx(ichimoku, self.ProcessCandle) \
.Start()
self.StartProtection(
takeProfit=Unit(float(self.TakeProfitPercent), UnitTypes.Percent),
stopLoss=Unit(float(self.StopLossPercent), UnitTypes.Percent),
useMarketOrders=True
)
def ProcessCandle(self, candle, ichimoku_value):
if candle.State != CandleStates.Finished:
return
if not ichimoku_value.IsFormed:
return
chikou = ichimoku_value.Chinkou
span_b = ichimoku_value.SenkouB
tenkan = ichimoku_value.Tenkan
kijun = ichimoku_value.Kijun
if chikou is None or span_b is None or tenkan is None or kijun is None:
return
osc = (chikou - span_b) - (tenkan - kijun)
t = candle.OpenTime
ema_result = process_float(self._ema, osc, t, True)
if not ema_result.IsFormed:
return
current = float(ema_result)
if self._prev_value is not None and self._prev_prev_value is not None:
prev = self._prev_value
prev_prev = self._prev_prev_value
rising = prev > prev_prev
falling = prev < prev_prev
if rising and current >= prev and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif falling and current <= prev and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_prev_value = self._prev_value
self._prev_value = current
def OnReseted(self):
super(ichimoku_oscillator_strategy, self).OnReseted()
self._ema = None
self._prev_value = None
self._prev_prev_value = None
def CreateClone(self):
return ichimoku_oscillator_strategy()