在 GitHub 上查看

多空蜡烛马丁策略

概述

该策略针对强烈的多头和空头蜡烛做出反应,并在同方向开仓。多头与空头各自维护独立的马丁格尔序列:亏损的多头交易会使用“多头放大倍数”增加下一笔多头仓位,而亏损的空头则使用“空头放大倍数”。止损与止盈距离也分别配置,从而忠实再现原始 MQL EA 的非对称参数。

交易逻辑

  1. 订阅设定的蜡烛类型(默认 1 分钟),只处理收盘完成的蜡烛。
  2. 当没有持仓时:
    • 多头信号: Close > Open 且蜡烛实体大于多头过滤阈值时,市价买入。
    • 空头信号: Close < Open 且实体大于空头过滤阈值时,市价卖出。
  3. 每次入场都会按照品种最小价格步长把设定的点数转换为价格距离,自动挂出止损与止盈单。
  4. 持仓关闭后,将当前实现盈亏与上次基线对比:
    • 亏损会把对应方向的马丁仓位乘以放大倍数。
    • 盈利或保本会把该方向的仓位重置为初始手数。
  5. 只允许一笔持仓,持仓期间忽略新的信号,保持与原 EA 一致的单笔交易模式。

资金管理

  • 多头与空头的马丁序列彼此独立,某一方向的亏损不会影响另一方向的下一次下单。
  • 下单手数根据 VolumeStep 自动对齐,避免因不合规手数被拒单。
  • 调用 StartProtection(useMarketOrders: true) 让 StockSharp 负责止损/止盈委托的同步处理。

参数说明

参数 说明
Initial Volume 两个方向的马丁循环共用的起始手数。
Bull Multiplier 多头交易亏损后,下一笔多头仓位的放大倍数。
Bear Multiplier 空头交易亏损后,下一笔空头仓位的放大倍数。
Bull Stop Loss 多头止损距离(点)。会转换为价格差。
Bull Take Profit 多头止盈距离(点)。
Bear Stop Loss 空头止损距离(点)。
Bear Take Profit 空头止盈距离(点)。
Bull Body Filter 触发买单所需的最小多头蜡烛实体(点)。
Bear Body Filter 触发卖单所需的最小空头蜡烛实体(点)。
Candle Type 用于生成信号的时间框架(默认 1 分钟)。

使用提示

  • 请确保交易标的提供有效的 PriceStepVolumeStep。若 PriceStep 缺失,策略会默认使用 0.0001。
  • 马丁逻辑基于实现盈亏,因此即使手动平仓也会正确更新序列。
  • 优化时可重点调整实体过滤阈值与放大倍数,以便在响应速度和回撤之间取得平衡。
using System;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Bull/Bear Candle Martingale strategy: Bullish/bearish candle direction + EMA filter.
/// Buys after strong bullish candle when close crosses above EMA.
/// Sells after strong bearish candle when close crosses below EMA.
/// </summary>
public class BullBearCandleMartingaleStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaPeriod;

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

	public int EmaPeriod
	{
		get => _emaPeriod.Value;
		set => _emaPeriod.Value = value;
	}

	public BullBearCandleMartingaleStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");

		_emaPeriod = Param(nameof(EmaPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA period", "Indicators");
	}

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

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };

		decimal? prevClose = null;
		decimal? prevEma = null;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, (candle, emaVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				var close = candle.ClosePrice;
				var bullish = close > candle.OpenPrice;
				var bearish = close < candle.OpenPrice;
				var bodySize = Math.Abs(close - candle.OpenPrice);
				var range = candle.HighPrice - candle.LowPrice;

				// Require strong candle body (>50% of range)
				var strongCandle = range > 0 && bodySize / range > 0.5m;

				if (prevClose.HasValue && prevEma.HasValue && strongCandle)
				{
					var crossUp = prevClose.Value <= prevEma.Value && close > emaVal;
					var crossDown = prevClose.Value >= prevEma.Value && close < emaVal;

					if (bullish && crossUp && Position <= 0)
						BuyMarket();
					else if (bearish && crossDown && Position >= 0)
						SellMarket();
				}

				prevClose = close;
				prevEma = emaVal;
			})
			.Start();

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