Открыть на GitHub

Стратегия eKeyboardTrader

Обзор

Эта стратегия воспроизводит логику советника MetaTrader «eKeyboardTrader», используя высокоуровневый API StockSharp. В оригинале торговля выполнялась через горячие клавиши и текстовые подсказки на графике. В версии для StockSharp взаимодействие перенесено в параметры стратегии, при этом последовательность ручных действий, проверки безопасности и настройка защитных ордеров сохранены.

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

  1. Подписка на Level1 — стратегия подписывается на поток лучших бид/аск котировок и использует их для проверки готовности к исполнению ручных команд.
  2. Ручные команды — три булевых параметра (BuyRequest, SellRequest, CloseRequest) заменяют сочетания клавиш B, S и C. При установке значения true выполняется соответствующая рыночная операция, после чего флаг автоматически сбрасывается.
  3. Ограничение частоты — между командами действует пауза в одну секунду, что защищает от повторной отправки, как и в оригинальном советнике.
  4. Защита позиций — расстояния до стоп-лосса и тейк-профита задаются в пунктах MetaTrader и переводятся в абсолютные цены через Security.PriceStep. При наличии хотя бы одного защитного расстояния включается StartProtection, благодаря чему каждая сделка сопровождается выставлением защитных заявок.
  5. Учёт проскальзывания — параметр SlippagePoints сохранён для совместимости и выводится в лог при каждой ручной сделке, аналогично комментариям советника.

Параметры

Параметр Описание
OrderVolume Базовый объём для ручных рыночных заявок.
StopLossPoints Дистанция до защитного стоп-лосса в пунктах MetaTrader (0 — отключено).
TakeProfitPoints Дистанция до защитного тейк-профита в пунктах MetaTrader (0 — отключено).
SlippagePoints Информационный допуск по проскальзыванию, выводимый в лог.
BuyRequest Установите true, чтобы отправить рыночную покупку (с последующим сбросом).
SellRequest Установите true, чтобы отправить рыночную продажу (с последующим сбросом).
CloseRequest Установите true, чтобы закрыть нетто-позицию по рынку (с последующим сбросом).

Отличия от версии MQL

  • На графике нет текстовых подсказок и звуковых сигналов — информацию о действиях можно отслеживать через лог.
  • Защитные заявки управляются через StartProtection, который закрывает позицию рыночными ордерами при достижении порога, вместо модификации отдельных тикетов.
  • Горячие клавиши заменены параметрами. Интерфейс, размещающий стратегию, может привязать к ним любые элементы управления.
  • Диагностика торговых запросов в MetaTrader сведена к лаконичным сообщениям в журнале.

Рекомендации по использованию

  • Перед стартом стратегии обязательно назначьте Security и Portfolio, как это требует исходный советник.
  • Проверка флагов выполняется при поступлении новых данных Level1, поэтому в спокойном рынке исполнение произойдёт на ближайшей котировке.
  • Изменение StopLossPoints или TakeProfitPoints во время работы потребует перезапуска стратегии, чтобы переинициализировать модуль защиты.
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// eKeyboard Trader strategy: CCI trend following.
/// Buys when CCI crosses above +100, sells when CCI crosses below -100.
/// </summary>
public class EKeyboardTraderStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cciPeriod;

	private decimal _prevCci;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }

	public EKeyboardTraderStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_cciPeriod = Param(nameof(CciPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("CCI Period", "CCI period", "Indicators");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevCci = 0m;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_hasPrev = false;
		var cci = new CommodityChannelIndex { Length = CciPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(cci, ProcessCandle).Start();
	}

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

		if (_hasPrev)
		{
			if (_prevCci <= 100 && cci > 100 && Position <= 0) BuyMarket();
			else if (_prevCci >= -100 && cci < -100 && Position >= 0) SellMarket();
		}

		_prevCci = cci;
		_hasPrev = true;
	}
}