在 GitHub 上查看

Lossless MA 策略

该策略基于快慢两条简单移动平均线(SMA)的交叉进行交易。 当出现反向信号时,可选择将亏损持仓移动到保本价而不是直接平仓,从而避免实现亏损。

工作原理

  1. 指标
    • 快速 SMA
    • 慢速 SMA
  2. 入场
    • 快速 SMA > 慢速 SMA 且当前方向不是做多时开多。
    • 快速 SMA < 慢速 SMA 且当前方向不是做空时开空。
    • 如果关闭 Close Losses 并且当前持仓数量少于 Max Deals,允许加仓。
  3. 出场
    • 当出现反向交叉。
    • 若启用 Close Losses,则立即平仓。
    • 若关闭 Close Losses 且交易处于亏损状态,则在入场价挂出限价单以保本离场。

参数

名称 说明 默认值
FastLength 快速 SMA 周期。 10
SlowLength 慢速 SMA 周期。 30
MaxDeals 最大同时持仓数。 5
CloseLosses 是否立即平掉亏损单。 true
Volume 订单量。 1
CandleType 计算所用的K线类型。 1分钟

说明

策略使用市价单进出场。当关闭 Close Losses 时,会在入场价挂出限价单以避免亏损,而不是直接止损。

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>
/// Lossless Moving Average strategy.
/// Trades fast and slow SMA crossovers.
/// </summary>
public class LosslessMaStrategy : Strategy
{
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _prevFast;
	private decimal? _prevSlow;

	/// <summary>
	/// Fast SMA period.
	/// </summary>
	public int FastLength { get => _fastLength.Value; set => _fastLength.Value = value; }

	/// <summary>
	/// Slow SMA period.
	/// </summary>
	public int SlowLength { get => _slowLength.Value; set => _slowLength.Value = value; }

	/// <summary>
	/// Type of candles used for calculations.
	/// </summary>
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	/// <summary>
	/// Constructor.
	/// </summary>
	public LosslessMaStrategy()
	{
		_fastLength = Param(nameof(FastLength), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast MA", "Fast SMA length", "Parameters");

		_slowLength = Param(nameof(SlowLength), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow MA", "Slow SMA length", "Parameters");

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

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

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

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

		var fastMa = new ExponentialMovingAverage { Length = FastLength };
		var slowMa = new ExponentialMovingAverage { Length = SlowLength };

		var subscription = SubscribeCandles(CandleType);

		subscription
			.Bind(fastMa, slowMa, ProcessCandle)
			.Start();

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var prevFast = _prevFast;
		var prevSlow = _prevSlow;

		_prevFast = fastValue;
		_prevSlow = slowValue;

		if (prevFast is null || prevSlow is null)
			return;

		// Bullish crossover
		if (prevFast <= prevSlow && fastValue > slowValue && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}

		// Bearish crossover
		if (prevFast >= prevSlow && fastValue < slowValue && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}
	}
}