Эта стратегия представляет собой порт эксперта MetaTrader ichimok2005, адаптированный под высокоуровневый API StockSharp. Алгоритм ищет уверенные пробои выше или ниже линии Senkou Span B индикатора Ichimoku и подтверждает импульс серией направленных свечей.
Логика торговли
Условия для покупки
Анализируются последние Shift + 2 завершённых свечей (при значении Shift = 1 это три предыдущих бара).
Необходимо, чтобы:
Самая дальняя свеча (Shift + 2) открылась ниже Senkou Span B.
Средняя свеча (Shift + 1) открылась и закрылась выше Senkou Span B.
Последняя свеча (Shift) открылась и закрылась выше Senkou Span B.
Две последние свечи были бычьими (цена закрытия выше цены открытия).
Запаздывающая линия Chinkou не должна находиться внутри облака, когда Senkou Span A расположена ниже Senkou Span B. Это повторяет фильтр исходного эксперта, который избегает боковых фаз.
При наличии короткой позиции она закрывается. Если предыдущий сигнал не был покупкой, открывается новая длинная позиция.
Условия для продажи
Симметричные критерии в противоположном направлении:
Свеча Shift + 2 открывается выше Senkou Span B.
Свеча Shift + 1 открывается и закрывается ниже Senkou Span B.
Свеча Shift открывается и закрывается ниже Senkou Span B.
Две последние свечи являются медвежьими (цена закрытия ниже цены открытия).
Линия Chinkou должна оставаться вне облака, если Senkou Span A ниже Senkou Span B.
При наличии длинной позиции она закрывается. Если предыдущий сигнал не был продажей, открывается новая короткая позиция.
Стоп-лосс и тейк-профит задаются в шагах цены и автоматически преобразуются в абсолютные расстояния через PriceStep. Для имитации поведения MetaTrader используются защитные рыночные заявки.
Управление размером позиции
Оригинальный советник поддерживал два режима:
Фиксированный объём (UseMoneyManagement = false): сделки исполняются объёмом OrderVolume (по умолчанию 0.1 лота).
Управление риском (UseMoneyManagement = true): размер позиции рассчитывается от текущей стоимости портфеля и процента MaximumRisk. Результат округляется к шагу лота инструмента и не опускается ниже одного шага.
Параметры
Параметр
Описание
Значение по умолчанию
StopLossPoints
Расстояние стоп-лосса в шагах цены.
30
TakeProfitPoints
Расстояние тейк-профита в шагах цены.
60
Shift
Смещение по барам при проверке структуры пробоя.
1
OrderVolume
Фиксированный объём при отключённом управлении риском.
0.1
MaximumRisk
Процент портфеля, используемый при расчёте объёма.
10
UseMoneyManagement
Включает управление риском.
false
TenkanPeriod
Период линии Tenkan-sen индикатора Ichimoku.
9
KijunPeriod
Период линии Kijun-sen индикатора Ichimoku.
26
SenkouBPeriod
Период линии Senkou Span B индикатора Ichimoku.
52
CandleType
Таймфрейм расчётов (по умолчанию часовые свечи).
1 час
Примечания
Обрабатываются только завершённые свечи, поэтому значения Ichimoku всегда окончательные.
Переменная _lastSignal предотвращает повторное открытие сделки в том же направлении подряд, как и в оригинальном советнике.
Если инструмент не задаёт PriceStep, расстояния стоп-лосса и тейк-профита трактуются как абсолютные значения цены.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class Ichimoku2005Strategy : Strategy
{
private readonly StrategyParam<int> _channelPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevMid;
private bool _hasPrev;
private int _cooldownRemaining;
public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public Ichimoku2005Strategy()
{
_channelPeriod = Param(nameof(ChannelPeriod), 52).SetDisplay("Channel Period", "Channel lookback", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 150).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevMid = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_prevMid = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var highest = new Highest { Length = ChannelPeriod };
var lowest = new Lowest { Length = ChannelPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(highest, lowest, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal highest, decimal lowest)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
var mid = (highest + lowest) / 2;
if (!_hasPrev) { _prevClose = close; _prevMid = mid; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevClose = close;
_prevMid = mid;
return;
}
if (_prevClose <= _prevMid && close > mid && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (_prevClose >= _prevMid && close < mid && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_prevClose = close;
_prevMid = mid;
}
}
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 Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class ichimoku_2005_strategy(Strategy):
def __init__(self):
super(ichimoku_2005_strategy, self).__init__()
self._channel_period = self.Param("ChannelPeriod", 52) \
.SetDisplay("Channel Period", "Channel lookback", "Indicators")
self._cooldown_candles = self.Param("CooldownCandles", 150) \
.SetDisplay("Cooldown", "Candles between signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
self._cooldown_remaining = 0
@property
def channel_period(self):
return self._channel_period.Value
@property
def cooldown_candles(self):
return self._cooldown_candles.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ichimoku_2005_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(ichimoku_2005_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
self._cooldown_remaining = 0
highest = Highest()
highest.Length = self.channel_period
lowest = Lowest()
lowest.Length = self.channel_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(highest, lowest, self.process_candle).Start()
def process_candle(self, candle, highest, lowest):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
mid = (float(highest) + float(lowest)) / 2.0
if not self._has_prev:
self._prev_close = close
self._prev_mid = mid
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_close = close
self._prev_mid = mid
return
if self._prev_close <= self._prev_mid and close > mid and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_candles
elif self._prev_close >= self._prev_mid and close < mid and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_candles
self._prev_close = close
self._prev_mid = mid
def CreateClone(self):
return ichimoku_2005_strategy()