Открыть на GitHub

Стратегия Karakatica

Обзор

Karakatica — это среднесрочная трендовая стратегия, портированная из советника MetaTrader 4 «Exp_karakatica». По умолчанию она торгует EUR/USD на таймфрейме M15 и использует собственный сигнал, воспроизводящий логику индикатора «iKarakatica» через модель пересечения скользящей средней. Период сигнала пересчитывается на каждом баре, что позволяет динамически следовать за наиболее прибыльным режимом рынка.

Стратегия открывает сделки только рыночными ордерами и только при отсутствии позиции. Защитные ордера (стоп-лосс и тейк-профит) подключаются автоматически с помощью подсистемы защиты StockSharp.

Торговая логика

  1. Формирование сигнала – рассчитывается простая скользящая средняя (SMA) закрытий свечей. Быстрый сигнал на покупку появляется, когда предыдущая свеча закрылась ниже (или на уровне) SMA, а последняя завершившаяся свеча закрылась выше неё. Сигнал на продажу формируется зеркально. Для имитации MT4 значение всегда берётся с предыдущей завершённой свечи (смещение 1).
  2. Управление позицией
    • При появлении встречного сигнала текущая позиция немедленно закрывается рыночным ордером.
    • Новая сделка разрешается только при нулевой позиции и отсутствии блокировок со стороны оптимизатора. Повторные входы в том же направлении запрещены до тех пор, пока не появится подтверждённый обратный сигнал.
  3. Расчёт объёма – размер позиции определяется параметром Risk. Значение риска переводится в желаемый объём на основе текущей стоимости портфеля, после чего корректируется с учётом шага объёма инструмента. Тем самым воспроизводится методика расчёта лота из исходного советника.
  4. Защита сделки – дистанции стоп-лосса и тейк-профита задаются в пунктах и переводятся в абсолютные цены через умножение на шаг цены инструмента.

Адаптивная оптимизация

Советник постоянно переоптимизирует период сигнала, подстраиваясь под рыночные изменения:

  1. Каждые ReoptimizeEvery баров запускается историческое моделирование на отрезке длиной OptimizationDepth баров.
  2. Для каждого периода в диапазоне [OptimizationStart, OptimizationEnd] с шагом OptimizationStep рассчитывается симуляция модели пересечения SMA:
    • Виртуальная позиция открывается при получении сигнала и закрывается при встречном сигнале, после чего обновляется накопленная прибыль.
    • Отдельно ведётся статистика прибыли по длинным и коротким сделкам, а также суммарный результат.
  3. После проверки всех кандидатов применяются правила:
    • Если прибыль и в длинную, и в короткую сторону отрицательная, торговля блокируется до следующей оптимизации.
    • Если лучшие результаты для покупок и продаж равны, используется общий лучший период без ограничений по направлениям.
    • В противном случае остаётся разрешённым только направление с максимальной прибылью, и стратегия выбирает соответствующий период.

Для начала оптимизации необходимо накопить минимум OptimizationDepth + OptimizationEnd + 2 завершённых свечей. До этого момента торговля откладывается.

Параметры

Параметр Описание По умолчанию Оптимизация
Risk Процент от стоимости портфеля (на 1000 единиц), определяющий целевой объём. 0.5 Да
StopLossPoints Размер стоп-лосса в пунктах. 50 Да
TakeProfitPoints Размер тейк-профита в пунктах. 150 Да
Period Текущий период SMA для генерации сигналов. Изменяется оптимизатором. 70 Да
OptimizationDepth Количество баров в выборке для моделирования. 250 Нет
ReoptimizeEvery Частота запусков оптимизатора в барах. 50 Нет
OptimizationStart Минимальный период в поисковом диапазоне. 10 Нет
OptimizationStep Шаг изменения периода. 5 Нет
OptimizationEnd Максимальный период в диапазоне. 150 Нет
CandleType Тип свечей (по умолчанию 15-минутный таймфрейм). Свечи M15 Нет

Рекомендации

  • Стратегия рассчитана на EUR/USD и таймфрейм M15. При переносе на другой инструмент проверьте шаг цены, шаг объёма и оценку спрэда.
  • Для корректной работы оптимизатора желательны котировки с лучшими ценами Bid/Ask. Если они недоступны, стратегия использует шаг цены как оценку спрэда.
  • Учтите, что оптимизации требуется несколько сотен исторических баров. Дайте стратегии время загрузить данные перед включением торговли.

Файлы

  • CS/KarakaticaStrategy.cs — реализация стратегии для StockSharp.
  • README.md — описание на английском языке.
  • README_ru.md — описание на русском языке (этот файл).
  • README_zh.md — описание на китайском языке.
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Karakatica strategy - adaptive SMA crossover with RSI filter.
/// Buys on fast/slow SMA bullish crossover with RSI above 50.
/// Sells on bearish crossover with RSI below 50.
/// </summary>
public class KarakaticaStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public KarakaticaStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 5)
			.SetDisplay("Fast SMA", "Fast SMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 15)
			.SetDisplay("Slow SMA", "Slow SMA period", "Indicators");

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetDisplay("RSI Period", "RSI period", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevFast = 0m; _prevSlow = 0m; _hasPrev = false; }

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_hasPrev = false;

		var fast = new SimpleMovingAverage { Length = FastPeriod };
		var slow = new SimpleMovingAverage { Length = SlowPeriod };
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, rsi, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal rsi)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_hasPrev)
		{
			_prevFast = fast;
			_prevSlow = slow;
			_hasPrev = true;
			return;
		}

		var crossUp = _prevFast <= _prevSlow && fast > slow;
		var crossDown = _prevFast >= _prevSlow && fast < slow;

		if (crossUp && rsi > 50 && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (crossDown && rsi < 50 && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevFast = fast;
		_prevSlow = slow;
	}
}