GitHub で見る

3Commas HA & MA

Uses Heikin Ashi candles and a pair of exponential moving averages. A long trade occurs when a bearish HA candle is followed by a bullish one while the fast MA is above the slow MA. Shorts follow the opposite setup. Positions are closed when price crosses the slow MA or hits the swing stop.

Details

  • Entry Criteria: Heikin Ashi reversal with MA confirmation.
  • Long/Short: Both directions.
  • Exit Criteria: Price crosses slow MA or stop.
  • Stops: Swing high/low.
  • Default Values:
    • MaFast = 9
    • MaSlow = 18
    • CandleType = TimeSpan.FromMinutes(1)
  • Filters:
    • Category: Trend
    • Direction: Both
    • Indicators: Heikin Ashi, EMA
    • Stops: Yes
    • Complexity: Intermediate
    • Timeframe: Intraday
    • Seasonality: No
    • Neural Networks: No
    • Divergence: No
    • Risk Level: Medium
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;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Heikin Ashi with moving averages.
/// </summary>
public class ThreeCommasHaMaStrategy : Strategy
{
	private readonly StrategyParam<int> _maFast;
	private readonly StrategyParam<int> _maSlow;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _haOpenPrev;
	private decimal _haClosePrev;
	private decimal _stopPrice;

	public int MaFast
	{
		get => _maFast.Value;
		set => _maFast.Value = value;
	}

	public int MaSlow
	{
		get => _maSlow.Value;
		set => _maSlow.Value = value;
	}

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

	public ThreeCommasHaMaStrategy()
	{
		_maFast = Param(nameof(MaFast), 9)
			.SetDisplay("MA Fast", "Fast moving average period", "MA");
		_maSlow = Param(nameof(MaSlow), 18)
			.SetDisplay("MA Slow", "Slow moving average period", "MA");
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_haOpenPrev = 0m;
		_haClosePrev = 0m;
		_stopPrice = 0m;
	}

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

		var ma1 = new ExponentialMovingAverage { Length = MaFast };
		var ma2 = new ExponentialMovingAverage { Length = MaSlow };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ma1, ma2, ProcessCandle)
			.Start();

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

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

		var haClose = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 4m;
		var haOpen = (_haOpenPrev == 0m && _haClosePrev == 0m) ? (candle.OpenPrice + candle.ClosePrice) / 2m : (_haOpenPrev + _haClosePrev) / 2m;
		var haBull = haClose > haOpen;
		var haBearPrev = _haClosePrev < _haOpenPrev;
		var haBullPrev = _haClosePrev > _haOpenPrev;

		if (haBearPrev && ma1 > ma2 && haBull && candle.ClosePrice > ma1 && Position <= 0)
		{
			_stopPrice = candle.LowPrice;
			BuyMarket();
		}
		else if (haBullPrev && ma1 < ma2 && !haBull && candle.ClosePrice < ma1 && Position >= 0)
		{
			_stopPrice = candle.HighPrice;
			SellMarket();
		}

		if (Position > 0 && candle.ClosePrice < ma2)
			SellMarket();
		else if (Position < 0 && candle.ClosePrice > ma2)
			BuyMarket();

		_haOpenPrev = haOpen;
		_haClosePrev = haClose;
	}
}