Стратегия представляет собой перенос эксперта MetaTrader TenKijun.mq4 на высокоуровневый API StockSharp. В оригинале советник лишь отслеживал пересечения линий Tenkan-sen и Kijun-sen индикатора Ишимоку и отправлял уведомления, не совершая сделок. Перенос полностью сохраняет уведомительный характер, дополняя его инфраструктурой StockSharp: параметрами, подпиской на свечи и автоматической визуализацией.
Алгоритм работает на завершённых свечах выбранного таймфрейма. Когда новая свеча закрывается в пределах разрешённого торгового интервала, рассчитывается индикатор Ichimoku с классическими периодами 9/26/52 и фиксируются текущие значения Tenkan/Kijun. Если Tenkan пересекает Kijun снизу вверх — в журнал добавляется запись о бычьем сигнале, при пересечении сверху вниз — запись о медвежьем сигнале. Сделки не открываются: стратегия предназначена для сигнализации или подключения внешней автоматики.
Индикаторы и поток данных
Индикатор — стандартный Ichimoku из StockSharp с независимыми параметрами Tenkan, Kijun и Senkou Span B. Для принятия решений используются только линии Tenkan и Kijun, как в исходном советнике.
Подписка — SubscribeCandles с настраиваемым CandleType. По умолчанию используется 30-минутный таймфрейм.
Связка — применяется BindEx, чтобы получать типизированный IchimokuValue без вызовов GetValue.
График — при наличии окна графика автоматически отображаются свечи и линия Ichimoku, что упрощает проверку сигналов.
Фильтр торговой сессии
Как и в MetaTrader, пользователь задаёт временной интервал в часах:
StartHour — начало активного окна (включительно), по умолчанию 0.
LastHour — окончание активного окна (включительно), по умолчанию 20.
Если StartHour ≤ LastHour, уведомления формируются только внутри этого диапазона. Если StartHour > LastHour, интервал считается ночным и покрывает конец суток и начало следующего дня (например, 20 → 6).
Параметры
Параметр
Описание
Значение по умолчанию
Примечание
StartHour
Старт часа, с которого разрешены уведомления
0
Целое число 0-23
LastHour
Финальный час разрешённого интервала
20
Целое число 0-23
TenkanPeriod
Длина расчёта линии Tenkan
9
Допускает оптимизацию
KijunPeriod
Длина расчёта линии Kijun
26
Допускает оптимизацию
SenkouSpanBPeriod
Длина Senkou Span B
52
Добавлена для полноты, в логике не участвует
CandleType
Тип свечей для индикатора
30-минутные свечи
Можно выбрать любой таймфрейм на основе TimeSpan
Логика сигналов
Дождаться первой завершённой свечи, чтобы инициализировать предыдущие значения Tenkan и Kijun.
На каждой последующей завершённой свече внутри торгового окна:
Извлечь текущие значения Tenkan и Kijun из IchimokuValue.
Зафиксировать бычий сигнал, если раньше Tenkan ≤ Kijun, а теперь Tenkan > Kijun.
Зафиксировать медвежий сигнал, если раньше Tenkan ≥ Kijun, а теперь Tenkan < Kijun.
Записать информационное сообщение в журнал с направлением сигнала, ценой закрытия и отметкой времени.
Рекомендации по применению
Подпишитесь на журнал стратегии или расширьте метод ProcessCandle, чтобы пересылать уведомления по email, в мессенджеры или проигрывать звук.
Для автоматической торговли унаследуйте стратегию и добавьте в обработчик размещение заявок (BuyMarket, SellMarket и т.п.).
Выберите таймфрейм, соответствующий используемому в MetaTrader графику, чтобы получать те же пересечения.
Отличия от оригинального советника
Уведомления пишутся через систему логирования StockSharp вместо SendNotification MetaTrader.
Все параметры снабжены метаданными (SetDisplay, диапазоны, признаки оптимизации), что упрощает работу в Designer/Optimizer.
Свечи и индикатор автоматически выводятся на график StockSharp.
Файлы
CS/TenKijunCrossStrategy.cs — реализация стратегии на C#.
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>
/// Tenkan/Kijun cross strategy based on Ichimoku indicator.
/// Buys when Tenkan crosses above Kijun, sells when Tenkan crosses below Kijun.
/// Uses SMA proxies for Tenkan (short) and Kijun (long) since Ichimoku complex type
/// requires BindEx and special value handling.
/// </summary>
public class TenKijunCrossStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _tenkanPeriod;
private readonly StrategyParam<int> _kijunPeriod;
private readonly Queue<decimal> _highsTenkan = new();
private readonly Queue<decimal> _lowsTenkan = new();
private readonly Queue<decimal> _highsKijun = new();
private readonly Queue<decimal> _lowsKijun = new();
private decimal? _prevTenkan;
private decimal? _prevKijun;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int TenkanPeriod
{
get => _tenkanPeriod.Value;
set => _tenkanPeriod.Value = value;
}
public int KijunPeriod
{
get => _kijunPeriod.Value;
set => _kijunPeriod.Value = value;
}
public TenKijunCrossStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for Ichimoku calculations", "General");
_tenkanPeriod = Param(nameof(TenkanPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Tenkan Period", "Tenkan-sen conversion line period", "Indicators");
_kijunPeriod = Param(nameof(KijunPeriod), 34)
.SetGreaterThanZero()
.SetDisplay("Kijun Period", "Kijun-sen base line period", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevTenkan = null;
_prevKijun = null;
_highsTenkan.Clear();
_lowsTenkan.Clear();
_highsKijun.Clear();
_lowsKijun.Clear();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// Compute Tenkan-sen = (highest high + lowest low) / 2 over TenkanPeriod
_highsTenkan.Enqueue(candle.HighPrice);
_lowsTenkan.Enqueue(candle.LowPrice);
if (_highsTenkan.Count > TenkanPeriod)
{
_highsTenkan.Dequeue();
_lowsTenkan.Dequeue();
}
// Compute Kijun-sen = (highest high + lowest low) / 2 over KijunPeriod
_highsKijun.Enqueue(candle.HighPrice);
_lowsKijun.Enqueue(candle.LowPrice);
if (_highsKijun.Count > KijunPeriod)
{
_highsKijun.Dequeue();
_lowsKijun.Dequeue();
}
if (_highsTenkan.Count < TenkanPeriod || _highsKijun.Count < KijunPeriod)
return;
var highsTenkan = _highsTenkan.ToArray();
var lowsTenkan = _lowsTenkan.ToArray();
var highsKijun = _highsKijun.ToArray();
var lowsKijun = _lowsKijun.ToArray();
var tenkan = (Max(highsTenkan) + Min(lowsTenkan)) / 2;
var kijun = (Max(highsKijun) + Min(lowsKijun)) / 2;
if (_prevTenkan is null || _prevKijun is null)
{
_prevTenkan = tenkan;
_prevKijun = kijun;
return;
}
var volume = Volume;
if (volume <= 0)
volume = 1;
var crossUp = _prevTenkan.Value <= _prevKijun.Value && tenkan > kijun;
var crossDown = _prevTenkan.Value >= _prevKijun.Value && tenkan < kijun;
if (crossUp)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
else if (crossDown)
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
_prevTenkan = tenkan;
_prevKijun = kijun;
}
private static decimal Max(IEnumerable<decimal> values)
{
decimal max = decimal.MinValue;
foreach (var v in values)
if (v > max) max = v;
return max;
}
private static decimal Min(IEnumerable<decimal> values)
{
decimal min = decimal.MaxValue;
foreach (var v in values)
if (v < min) min = v;
return min;
}
/// <inheritdoc />
protected override void OnReseted()
{
_prevTenkan = null;
_prevKijun = null;
_highsTenkan.Clear();
_lowsTenkan.Clear();
_highsKijun.Clear();
_lowsKijun.Clear();
base.OnReseted();
}
}