Auf GitHub ansehen

IU Higher-Timeframe-MA-Kreuz-Strategie

Die IU Higher Timeframe MA Cross Strategie handelt, wenn ein schneller gleitender Durchschnitt, der auf einem benutzerdefinierten Zeitrahmen berechnet wird, einen langsameren gleitenden Durchschnitt aus einem möglicherweise anderen Zeitrahmen kreuzt. Eine Long-Position wird bei einem bullischen Kreuz eröffnet und eine Short-Position bei einem bärischen Kreuz. Der Stop-Loss wird am Extrempunkt der vorherigen Kerze platziert, und der Take Profit verwendet ein konfigurierbares Risiko-Ertrags-Verhältnis.

Details

  • Daten: Kerzen aus angegebenen Zeitrahmen.
  • Einstiegskriterien:
    • Long: MA1 kreuzt über MA2.
    • Short: MA1 kreuzt unter MA2.
  • Ausstiegskriterien: Stop-Loss oder Take Profit erreicht.
  • Stops: Vorheriges Kerzenhoch/-tief mit RiskToReward-Multiplikator.
  • Standardwerte:
    • Ma1CandleType = 60m
    • Ma1Length = 20
    • Ma1Type = MovingAverageTypeEnum.Exponential
    • Ma2CandleType = 60m
    • Ma2Length = 50
    • Ma2Type = MovingAverageTypeEnum.Exponential
    • RiskToReward = 2
  • Filter:
    • Kategorie: Trend
    • Richtung: Long & Short
    • Indikatoren: Gleitender Durchschnitt
    • Komplexität: Niedrig
    • Risikolevel: Mittel
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
using StockSharp.Algo;

namespace StockSharp.Samples.Strategies;



public class IUHigherTimeframeMACrossStrategy : Strategy
{
	private readonly StrategyParam<decimal> _riskToReward;
	private readonly StrategyParam<DataType> _ma1CandleType;
	private readonly StrategyParam<int> _ma1Length;
	private readonly StrategyParam<MovingAverageTypes> _ma1Type;
	private readonly StrategyParam<DataType> _ma2CandleType;
	private readonly StrategyParam<int> _ma2Length;
	private readonly StrategyParam<MovingAverageTypes> _ma2Type;

	private decimal? _ma1;
	private decimal? _ma2;
	private decimal? _prevMa1;
	private decimal? _prevMa2;

	private decimal? _entryPrice;
	private decimal? _stopPrice;
	private decimal? _takePrice;

	private decimal? _prevLow;
	private decimal? _prevHigh;

	private ICandleMessage _lastMa1Candle;

	private DecimalLengthIndicator _ma1Indicator;
	private DecimalLengthIndicator _ma2Indicator;

	public IUHigherTimeframeMACrossStrategy()
	{
		_riskToReward = Param(nameof(RiskToReward), 2m)
		.SetGreaterThanZero()
		.SetDisplay("RTR", "Risk to reward ratio", "Protection")
		
		.SetOptimize(1m, 5m, 0.5m);

		_ma1CandleType = Param(nameof(Ma1CandleType), TimeSpan.FromMinutes(60).TimeFrame())
		.SetDisplay("MA1 Timeframe", "Timeframe for first MA", "Moving Averages");

		_ma1Length = Param(nameof(Ma1Length), 20)
		.SetGreaterThanZero()
		.SetDisplay("MA1 Length", "Period for first MA", "Moving Averages")
		
		.SetOptimize(5, 100, 5);

		_ma1Type = Param(nameof(Ma1Type), MovingAverageTypes.Exponential)
		.SetDisplay("MA1 Type", "Type of first MA", "Moving Averages");

		_ma2CandleType = Param(nameof(Ma2CandleType), TimeSpan.FromMinutes(60).TimeFrame())
		.SetDisplay("MA2 Timeframe", "Timeframe for second MA", "Moving Averages");

		_ma2Length = Param(nameof(Ma2Length), 50)
		.SetGreaterThanZero()
		.SetDisplay("MA2 Length", "Period for second MA", "Moving Averages")
		
		.SetOptimize(10, 200, 5);

		_ma2Type = Param(nameof(Ma2Type), MovingAverageTypes.Exponential)
		.SetDisplay("MA2 Type", "Type of second MA", "Moving Averages");
	}

	public decimal RiskToReward { get => _riskToReward.Value; set => _riskToReward.Value = value; }
	public DataType Ma1CandleType { get => _ma1CandleType.Value; set => _ma1CandleType.Value = value; }
	public int Ma1Length { get => _ma1Length.Value; set => _ma1Length.Value = value; }
	public MovingAverageTypes Ma1Type { get => _ma1Type.Value; set => _ma1Type.Value = value; }
	public DataType Ma2CandleType { get => _ma2CandleType.Value; set => _ma2CandleType.Value = value; }
	public int Ma2Length { get => _ma2Length.Value; set => _ma2Length.Value = value; }
	public MovingAverageTypes Ma2Type { get => _ma2Type.Value; set => _ma2Type.Value = value; }

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

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

		_ma1 = null;
		_ma2 = null;
		_prevMa1 = null;
		_prevMa2 = null;
		_entryPrice = null;
		_stopPrice = null;
		_takePrice = null;
		_prevLow = null;
		_prevHigh = null;
		_lastMa1Candle = null;
		_ma1Indicator = null;
		_ma2Indicator = null;
	}

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

		_ma1Indicator = CreateMa(Ma1Type, Ma1Length);
		_ma2Indicator = CreateMa(Ma2Type, Ma2Length);

		var ma1Sub = SubscribeCandles(Ma1CandleType);
		ma1Sub.Bind(_ma1Indicator, ProcessMa1).Start();

		var ma2Sub = SubscribeCandles(Ma2CandleType);
		ma2Sub.Bind(_ma2Indicator, ProcessMa2).Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, ma1Sub);
			DrawIndicator(area, _ma1Indicator);
			DrawIndicator(area, _ma2Indicator);
			DrawOwnTrades(area);
		}
	}

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

		if (_lastMa1Candle != null)
		{
			_prevLow = _lastMa1Candle.LowPrice;
			_prevHigh = _lastMa1Candle.HighPrice;
		}
		_lastMa1Candle = candle;

		_prevMa1 = _ma1;
		_ma1 = value;

		if (Position > 0)
		{
			if (_stopPrice.HasValue && candle.LowPrice <= _stopPrice)
				CloseLong();
			else if (_takePrice.HasValue && candle.HighPrice >= _takePrice)
				CloseLong();
		}
		else if (Position < 0)
		{
			if (_stopPrice.HasValue && candle.HighPrice >= _stopPrice)
				CloseShort();
			else if (_takePrice.HasValue && candle.LowPrice <= _takePrice)
				CloseShort();
		}

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_prevMa1.HasValue && _prevMa2.HasValue && _ma1.HasValue && _ma2.HasValue && Position == 0)
		{
			var crossUp = _prevMa1 < _prevMa2 && _ma1 > _ma2;
			var crossDown = _prevMa1 > _prevMa2 && _ma1 < _ma2;

			if (crossUp)
			{
				BuyMarket();
				_entryPrice = candle.ClosePrice;
				var sl = _prevLow ?? candle.LowPrice;
				_stopPrice = sl;
				_takePrice = (_entryPrice - sl) * RiskToReward + _entryPrice;
			}
			else if (crossDown)
			{
				SellMarket();
				_entryPrice = candle.ClosePrice;
				var sl = _prevHigh ?? candle.HighPrice;
				_stopPrice = sl;
				_takePrice = _entryPrice - (sl - _entryPrice) * RiskToReward;
			}
		}
	}

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

		_prevMa2 = _ma2;
		_ma2 = value;
	}

	private void CloseLong()
	{
		SellMarket(Position);
		ResetProtection();
	}

	private void CloseShort()
	{
		BuyMarket(Math.Abs(Position));
		ResetProtection();
	}

	private void ResetProtection()
	{
		_entryPrice = null;
		_stopPrice = null;
		_takePrice = null;
	}

	private static DecimalLengthIndicator CreateMa(MovingAverageTypes type, int length)
	{
		return type switch
		{
			MovingAverageTypes.Simple => new SMA { Length = length },
			MovingAverageTypes.Exponential => new EMA { Length = length },
			MovingAverageTypes.Smoothed => new SmoothedMovingAverage { Length = length },
			MovingAverageTypes.Weighted => new WeightedMovingAverage { Length = length },
			MovingAverageTypes.VolumeWeighted => new VolumeWeightedMovingAverage { Length = length },
			_ => new SMA { Length = length },
		};
	}

	public enum MovingAverageTypes
	{
		Simple,
		Exponential,
		Smoothed,
		Weighted,
		VolumeWeighted
	}
}