Открыть на GitHub

Стратегия Huge Income

Обзор

Стратегия представляет собой порт советника MetaTrader 4 «Huge Income» на платформу StockSharp. Исходный робот ищет внутридневные движения, которые значительно удаляются от дневного открытия, и открывает одну позицию в сторону прорыва. Версия для StockSharp воспроизводит эту логику: восстанавливает дневной максимум и минимум по внутридневным свечам, допускает только одну позицию одновременно и принудительно закрывает её незадолго до заданного времени окончания сессии.

Данные и окружение

  • Инструменты: любой инструмент с корректно заданным шагом цены (PriceStep). Алгоритм изначально написан для валютных пар, но после настройки параметров пипсов может применяться и к другим рынкам.
  • Таймфрейм: по умолчанию подписка идёт на 15-минутные свечи, чтобы восстановить дневное открытие, максимум и минимум. При необходимости можно выбрать другой тип свечей, если источник данных предоставляет более высокое разрешение.
  • Сессии: ожидается, что время свечей совпадает со временем торгового сервера, как и в MetaTrader. Настраивайте граничные часы в соответствии с этой временной зоной.

Логика торговли

  1. При появлении новой свечи пересчитываются показатели текущего дня. Первая свеча дня задаёт цену открытия и инициализирует экстремумы.
  2. В любой момент может существовать только одна позиция (длинная или короткая). Отложенные заявки не используются — стратегия опирается на рыночные приказы.
  3. Условия для покупки:
    • Текущая цена закрытия выше дневного открытия.
    • Расстояние между открытием и дневным минимумом превышает MinimumRangePips (переведённое в абсолютную цену через PriceStep).
    • Текущий час строго меньше BuyCutoffHour.
  4. Условия для продажи:
    • Текущая цена закрытия ниже дневного открытия.
    • Расстояние между дневным максимумом и открытием превышает MinimumRangePips.
    • Текущий час строго меньше SellCutoffHour.
  5. При выполнении условий отправляется рыночный ордер объёмом TradeVolume, после чего стратегия не рассматривает новые входы до полного закрытия позиции.
  6. После наступления MarketCloseHour любая открытая позиция закрывается рыночным приказом. Это повторяет цикл OrderClose из оригинального кода, предназначенный для закрытия перед завершением торговой недели.

Управление рисками и капиталом

  • TradeVolume — фиксированный объём сделки. В оригинальном советнике нет усреднения или мартингейла, поэтому порт также использует постоянный объём.
  • Отдельных стоп-лоссов и тейк-профитов не предусмотрено. Контроль риска обеспечивается фильтром дневного диапазона и принудительным закрытием позиции перед окончанием сессии. При необходимости стратегию можно расширить стоп-заявками или трейлингом.

Параметры

Параметр Описание
TradeVolume Объём позиции для рыночных приказов BuyMarket и SellMarket.
MinimumRangePips Минимальное расстояние (в пипсах) между дневным открытием и противоположным экстремумом, необходимое для входа. Конвертируется в абсолютную цену через Security.PriceStep.
BuyCutoffHour Последний час (0–23), когда допускаются новые покупки. Сравнение строгое (currentHour < BuyCutoffHour).
SellCutoffHour Последний час (0–23), когда допускаются новые продажи.
MarketCloseHour Час, в который все открытые позиции принудительно закрываются. Значение 23 соответствует поведению исходного советника по пятницам.
CandleType Тип свечей, используемых для подписки и восстановления дневной статистики.

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

  • StockSharp получает свечные данные вместо тиков. Если в MetaTrader стратегия работала по тикам, выбирайте достаточно мелкий таймфрейм свечей, чтобы сохранить скорость реакции.
  • Фильтр MinimumRangePips автоматически отключается, если у инструмента отсутствует PriceStep. В этом случае входы разрешены при любом прорыве открытия.
  • Все сделки исполняются рыночными приказами и немедленно закрываются в момент MarketCloseHour, что воспроизводит цикл OrderClose без использования отложенных ордеров.

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

  • Подберите таймфрейм свечей под желаемую скорость исполнения. Более короткие свечи точнее отслеживают дневные экстремумы, но требуют большего объёма данных.
  • Уточните торговые часы инструмента. Если рынок закрывается раньше указанного MarketCloseHour, принудительный выход произойдёт на следующей торговой сессии.
  • При необходимости добавьте портфельные или аккаунтные ограничения (например, через StartProtection), чтобы расширить контроль риска по сравнению с оригинальной версией.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Huge Income: EMA trend following with ATR stops.
/// </summary>
public class HugeIncomeStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevClose;
	private decimal _entryPrice;

	public HugeIncomeStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_emaLength = Param(nameof(EmaLength), 20)
			.SetDisplay("EMA Length", "Trend filter.", "Indicators");
		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
	public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevClose = 0; _entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevClose = 0; _entryPrice = 0;
		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ema, atr, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished) return;
		if (atrVal <= 0 || _prevClose == 0) { _prevClose = candle.ClosePrice; return; }
		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if (close < emaVal || close <= _entryPrice - atrVal * 2m) { SellMarket(); _entryPrice = 0; }
		}
		else if (Position < 0)
		{
			if (close > emaVal || close >= _entryPrice + atrVal * 2m) { BuyMarket(); _entryPrice = 0; }
		}

		if (Position == 0)
		{
			if (close > emaVal && _prevClose <= emaVal) { _entryPrice = close; BuyMarket(); }
			else if (close < emaVal && _prevClose >= emaVal) { _entryPrice = close; SellMarket(); }
		}
		_prevClose = close;
	}
}