Открыть на GitHub

FRASMAv2

Стратегия на основе Fractal Adaptive Simple Moving Average (FRASMAv2).

Стратегия вычисляет фрактально-адаптивное простое скользящее среднее с использованием индикатора Fractal Dimension. Цвет индикатора меняется в зависимости от наклона: зелёный при росте, серый при плоском движении, пурпурный при падении. Стратегия отслеживает смену цвета на последней завершённой свече:

  • Если индикатор был зелёным на предыдущем баре и стал не зелёным (серым или пурпурным) на последнем баре, стратегия закрывает короткие позиции и открывает новую длинную позицию.
  • Если индикатор был пурпурным и стал не пурпурным, стратегия закрывает длинные позиции и открывает новую короткую позицию.

Управление рисками осуществляется через параметры стоп-лосса и тейк-профита, задаваемые в пунктах.

Детали

  • Условия входа: Смена цвета FRASMAv2.
  • Длинные/короткие: Оба направления.
  • Условия выхода: Противоположная смена цвета.
  • Стопы: Тейк-профит и стоп-лосс через модуль защиты.
  • Значения по умолчанию:
    • Period = 30
    • TakeProfit = 2000 пунктов
    • StopLoss = 1000 пунктов
    • CandleType = TimeSpan.FromHours(4)
  • Фильтры:
    • Категория: Разворот тренда
    • Направление: Оба
    • Индикаторы: FractalDimension, FRASMAv2
    • Стопы: Да
    • Сложность: Средняя
    • Таймфрейм: 4 часа
    • Сезонность: Нет
    • Нейросети: Нет
    • Дивергенция: Нет
    • Уровень риска: Средний
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>
/// Strategy based on Fractal Adaptive Simple Moving Average (FRASMAv2).
/// Computes FRAMA from fractal dimension, trades on color (slope direction) changes.
/// </summary>
public class FrasmaV2Strategy : Strategy
{
	private readonly StrategyParam<int> _period;
	private readonly StrategyParam<DataType> _candleType;

	private bool _isFirst;
	private decimal _prevFrama;
	private int _prevColor;

	public int Period { get => _period.Value; set => _period.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public FrasmaV2Strategy()
	{
		_period = Param(nameof(Period), 30)
			.SetGreaterThanZero()
			.SetDisplay("Period", "FRAMA calculation period", "Indicator");

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

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_isFirst = true;
		_prevFrama = 0;
		_prevColor = 1;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_isFirst = true;
		_prevFrama = 0;
		_prevColor = 1;

		var fdi = new FractalDimension { Length = Period };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(fdi, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

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

		if (!fdiValue.IsFinal)
			return;

		var fdi = fdiValue.GetValue<decimal>();
		var alpha = (decimal)Math.Exp(-4.6 * ((double)fdi - 1.0));
		alpha = Math.Max(0.01m, Math.Min(1m, alpha));

		var price = candle.ClosePrice;
		var frama = _isFirst ? price : alpha * price + (1 - alpha) * _prevFrama;

		int color;
		if (_isFirst)
		{
			color = 1;
			_isFirst = false;
		}
		else if (frama > _prevFrama)
			color = 0; // Uptrend
		else if (frama < _prevFrama)
			color = 2; // Downtrend
		else
			color = 1; // Flat

		// Uptrend ended (color was 0, now > 0) -> sell signal
		if (_prevColor == 0 && color > 0 && Position >= 0)
			SellMarket();
		// Downtrend ended (color was 2, now < 2) -> buy signal
		else if (_prevColor == 2 && color < 2 && Position <= 0)
			BuyMarket();

		_prevFrama = frama;
		_prevColor = color;
	}
}