在 GitHub 上查看

EMA SAR Bulls Bears 策略

该策略结合了快慢指数移动平均线 (EMA)、抛物线 SAR 以及 Bulls/Bears Power 指标。策略仅在设定的日内时间段内交易,并使用简单的止盈止损保护。

当 EMA3 低于 EMA34、抛物线 SAR 位于蜡烛高点之上且 Bears Power 为负但上升时开空仓。相反条件下开多仓:EMA3 高于 EMA34、SAR 低于蜡烛低点且 Bulls Power 为正但下降。

细节

  • 入场条件
    • 做多:EMA3 高于 EMA34,SAR 低于蜡烛最低点,Bulls Power > 0 且下降。
    • 做空:EMA3 低于 EMA34,SAR 高于蜡烛最高点,Bears Power < 0 且上升。
  • 方向:多空皆可。
  • 出场条件:出现反向信号或触发止损/止盈。
  • 止损/止盈:是,使用固定止盈(400 点)和止损(2000 点)。
  • 过滤器
    • 仅在 09:00 至 17:00 之间交易。
    • 使用 15 分钟蜡烛。
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 combining EMA crossover, Parabolic SAR, and Bulls/Bears Power indicators.
/// </summary>
public class EmaSarBullsBearsStrategy : Strategy
{
	private readonly StrategyParam<int> _shortEmaPeriod;
	private readonly StrategyParam<int> _longEmaPeriod;
	private readonly StrategyParam<int> _bearsBullsPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevBears;
	private decimal _prevBulls;
	private bool _hasPrev;

	public int ShortEmaPeriod { get => _shortEmaPeriod.Value; set => _shortEmaPeriod.Value = value; }
	public int LongEmaPeriod { get => _longEmaPeriod.Value; set => _longEmaPeriod.Value = value; }
	public int BearsBullsPeriod { get => _bearsBullsPeriod.Value; set => _bearsBullsPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public EmaSarBullsBearsStrategy()
	{
		_shortEmaPeriod = Param(nameof(ShortEmaPeriod), 3)
			.SetGreaterThanZero()
			.SetDisplay("Short EMA", "Short EMA period", "Indicators");

		_longEmaPeriod = Param(nameof(LongEmaPeriod), 34)
			.SetGreaterThanZero()
			.SetDisplay("Long EMA", "Long EMA period", "Indicators");

		_bearsBullsPeriod = Param(nameof(BearsBullsPeriod), 13)
			.SetGreaterThanZero()
			.SetDisplay("Bulls/Bears Period", "Period for Bulls and Bears Power", "Indicators");

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

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevBears = 0;
		_prevBulls = 0;
		_hasPrev = false;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var shortEma = new ExponentialMovingAverage { Length = ShortEmaPeriod };
		var longEma = new ExponentialMovingAverage { Length = LongEmaPeriod };
		var sar = new ParabolicSar();
		var bearsPower = new BearPower { Length = BearsBullsPeriod };
		var bullsPower = new BullPower { Length = BearsBullsPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(shortEma, longEma, sar, bearsPower, bullsPower, ProcessCandle)
			.Start();

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

	private void ProcessCandle(ICandleMessage candle, decimal shortEma, decimal longEma, decimal sarValue, decimal bearsPower, decimal bullsPower)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_hasPrev)
		{
			_prevBears = bearsPower;
			_prevBulls = bullsPower;
			_hasPrev = true;
			return;
		}

		var shortSignal = shortEma < longEma && sarValue > candle.HighPrice && bearsPower < 0m &&
			bearsPower > _prevBears;

		var longSignal = shortEma > longEma && sarValue < candle.LowPrice && bullsPower > 0m &&
			bullsPower < _prevBulls;

		if (shortSignal && Position >= 0)
			SellMarket();
		else if (longSignal && Position <= 0)
			BuyMarket();

		_prevBears = bearsPower;
		_prevBulls = bullsPower;
	}
}