Открыть на GitHub

Стратегия AMA Trader

Общее описание

Стратегия AMA Trader воспроизводит логику оригинального советника MetaTrader 5 «AMA Trader». В основе лежит комбинация адаптивной скользящей средней Кауфмана (AMA) и индикатора RSI. Стратегия добавляет позицию против краткосрочных откатов, если цена остаётся по ту сторону AMA. Реализация на StockSharp использует высокоуровневый API, подписку на свечи и привязку индикаторов, поэтому поведение остаётся близким к исходной версии и одновременно полностью совместимым с инфраструктурой StockSharp.

Допущения

  • Инструмент: спот‑FX, CFD и другие трендовые рынки, где допустимо усреднение.
  • Таймфрейм: по умолчанию минутные свечи, можно изменить через параметр CandleType.
  • Сессии: специальных фильтров по времени нет, расчёт выполняется на каждой закрывшейся свече.

Индикаторы

  1. Kaufman Adaptive Moving Average (AMA)
    • Параметры AmaLength, AmaFastPeriod и AmaSlowPeriod задают сглаживание.
    • Определяет основное направление. Покупки рассматриваются только при закрытии выше AMA, продажи — при закрытии ниже AMA.
  2. Relative Strength Index (RSI)
    • Рассчитывается по закрытию свечи с периодом RsiLength.
    • Параметр StepLength задаёт, сколько последних значений RSI должны подтверждать сигнал. Значение 0 эквивалентно проверке только последнего значения (как в исходном советнике).
    • Уровни RsiLevelDown (30) и RsiLevelUp (70) определяют зоны перепроданности и перекупленности.

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

  1. Валидация бара
    • Торговля возможна только на закрытых свечах и при готовности стратегии (онлайн‑режим, разрешение на торги).
  2. Контроль прибыли до появления нового сигнала
    • При превышении плавающей прибыли порога ProfitTarget стратегия полностью закрывает позицию и ждёт следующего события.
    • При росте реализованной прибыли с момента последнего сброса больше, чем WithdrawalAmount, все позиции закрываются, а контрольная точка по прибыли обновляется. Это аналог TesterWithdrawal в MetaTrader, но без фактического вывода средств.
  3. Открытие длинной позиции
    • Условие: закрытие выше AMA и хотя бы одно значение RSI ниже RsiLevelDown.
    • Действие: рыночная покупка. Если текущая длинная позиция убыточна (отрицательная нереализованная PnL), выполняется дополнительная покупка для усреднения.
  4. Открытие короткой позиции
    • Условие: закрытие ниже AMA и хотя бы одно значение RSI выше RsiLevelUp.
    • Действие: рыночная продажа. При убытке по шорт‑позиции добавляется дополнительная продажа.
  5. Учёт позиций
    • Обработчик OnOwnTradeReceived отслеживает фактические сделки, поддерживает средние цены и объёмы для лонгов и шортов. Это позволяет корректно оценивать текущую PnL без запретных агрегирующих методов.

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

  • Объём усреднения: каждая сделка исполняется фиксированным объёмом LotSize. При убытке стратегия добавляет ещё один ордер в том же направлении.
  • Цель по плавающей прибыли: ProfitTarget (по умолчанию 50 денежных единиц) закрывает все позиции при достижении порога.
  • Контроль реализованной прибыли: WithdrawalAmount (по умолчанию 1000) закрывает позиции, когда накопленная реализованная прибыль превышает заданное значение; после закрытия контрольная точка обнуляется.
  • Защитные ордера: дополнительные стоп‑приказы не используются, при необходимости их следует добавить внешними средствами.

Параметры

Параметр Описание
CandleType Тип свечей/таймфрейм для расчёта индикаторов.
LotSize Объём каждой рыночной заявки.
RsiLength Период RSI.
StepLength Количество последних значений RSI для проверки (0 = только текущая свеча).
RsiLevelUp Уровень RSI для сигналов на продажу.
RsiLevelDown Уровень RSI для сигналов на покупку.
AmaLength Период сглаживания AMA.
AmaFastPeriod Быстрый коэффициент сглаживания AMA.
AmaSlowPeriod Медленный коэффициент сглаживания AMA.
ProfitTarget Порог плавающей прибыли для принудительного закрытия (0 отключает правило).
WithdrawalAmount Прирост реализованной прибыли для принудительного закрытия (0 отключает правило).

Особенности реализации на StockSharp

  • Используется высокоуровневый API: подписка через SubscribeCandles, привязка индикаторов методом .Bind, обработка готовых значений без ручного обращения к буферам.
  • Для оценки текущей PnL поддерживаются частные накопители объёма и средней цены, обновляемые в OnOwnTradeReceived.
  • Заявки отправляются методами BuyMarket и SellMarket, объём берётся из параметра LotSize. Для закрытия используется явная передача текущего объёма, чтобы ликвидировать как лонг, так и шорт.
  • Вместо проверки ask/bid, как в MetaTrader, берётся цена закрытия свечи — это максимально близкое приближение в свечной модели StockSharp.

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

  • WithdrawalAmount не вызывает TesterWithdrawal, а только обновляет внутреннюю точку отсчёта прибыли.
  • Параметры смещения AMA и выбор цены для расчёта не перенесены, поскольку стандартный индикатор StockSharp работает по цене закрытия.
  • Комиссии и свопы не добавляются вручную к расчёту плавающей прибыли — они учитываются торговой инфраструктурой при исполнении сделок.

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

  • При агрессивном усреднении целесообразно включить портфельные ограничения или модули защиты позиций.
  • Подбирайте параметры AMA и RSI под конкретный инструмент: для быстрых рынков полезно уменьшать период AMA и расширять пороги RSI.
  • При значении StepLength > 1 следите за просадкой — стратегия может добавлять несколько ордеров подряд во время сильного контртренда.
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>
/// AMA Trader strategy. Uses Kaufman Adaptive MA with price crossover.
/// </summary>
public class AmaTraderStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _amaPeriod;
	private decimal? _prevClose;
	private decimal? _prevAma;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int AmaPeriod { get => _amaPeriod.Value; set => _amaPeriod.Value = value; }

	public AmaTraderStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
		_amaPeriod = Param(nameof(AmaPeriod), 10).SetGreaterThanZero().SetDisplay("AMA Period", "Kaufman AMA period", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClose = null;
		_prevAma = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevClose = null; _prevAma = null;
		var ama = new KaufmanAdaptiveMovingAverage { Length = AmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ama, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ama); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal amaVal)
	{
		if (candle.State != CandleStates.Finished) return;
		var close = candle.ClosePrice;
		if (!IsFormedAndOnlineAndAllowTrading()) { _prevClose = close; _prevAma = amaVal; return; }
		if (_prevClose == null || _prevAma == null) { _prevClose = close; _prevAma = amaVal; return; }
		if (_prevClose.Value <= _prevAma.Value && close > amaVal && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
		else if (_prevClose.Value >= _prevAma.Value && close < amaVal && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
		_prevClose = close; _prevAma = amaVal;
	}
}