在 GitHub 上查看

Doji Trader 策略

该策略将 MQL4 的 "DojiTrader" 智能交易系统转换成 StockSharp C# 示例。策略会在欧美主要交易时段内寻找最近出现的十字星,并在突破十字星区间时进场。

交易逻辑

  • 策略只处理所选周期的已完成 K 线(默认使用 30 分钟 K 线)。
  • 仅在平台时间 08:00 至 17:00 之间允许开仓。
  • 空仓时向前回看最多三根已完成 K 线,记录最近一根开盘价等于收盘价的十字星。
  • 若紧随十字星的 K 线收盘价高于十字星最高价,则准备做多;若收盘价低于十字星最低价,则准备做空。
  • 之后一旦有新的 K 线收盘价再次越过触发价,策略即按突破方向发送市价单。
  • 建仓后会继续使用该十字星区间进行风控,当出现以下任一情况时平仓:
    • 前一根 K 线收盘价重新回到区间内(多头:收于十字星最低价下方;空头:收于十字星最高价上方)。
    • K 线极值触及模拟的止损或止盈价格,这些价格模仿原始 MQL4 策略中固定点差的退出方式。

参数

  • Order volume – 市价单交易量。
  • Take profit (steps) – 止盈距离,以价格最小变动单位计。
  • Stop loss (steps) – 止损距离,以价格最小变动单位计。
  • Candle type – 用于产生信号的 K 线周期。

止损和止盈均基于标的物的最小价格步长计算,以复现原始 EA 中固定点差的处理。当最近三根 K 线中没有新的十字星时,突破状态会被清除并重新开始搜索。

using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Doji Trader breakout strategy.
/// Detects doji candles and enters on breakout of doji range.
/// Uses SMA as trend filter for direction.
/// </summary>
public class DojiTraderBreakoutStrategy : Strategy
{
	private readonly StrategyParam<int> _smaPeriod;
	private readonly StrategyParam<decimal> _dojiRatio;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevHigh;
	private decimal _prevLow;
	private bool _prevWasDoji;
	private bool _hasPrev;

	public int SmaPeriod { get => _smaPeriod.Value; set => _smaPeriod.Value = value; }
	public decimal DojiRatio { get => _dojiRatio.Value; set => _dojiRatio.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public DojiTraderBreakoutStrategy()
	{
		_smaPeriod = Param(nameof(SmaPeriod), 20)
			.SetDisplay("SMA Period", "SMA period for trend filter", "Indicators");

		_dojiRatio = Param(nameof(DojiRatio), 0.25m)
			.SetDisplay("Doji Ratio", "Max body/range ratio for doji detection", "Indicators");

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

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevHigh = 0m; _prevLow = 0m; _prevWasDoji = false; _hasPrev = false; }

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

		_hasPrev = false;
		_prevWasDoji = false;

		var sma = new SimpleMovingAverage { Length = SmaPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(sma, ProcessCandle)
			.Start();
	}

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

		var range = candle.HighPrice - candle.LowPrice;
		var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
		var isDoji = range > 0 && body / range < DojiRatio;

		if (_hasPrev && _prevWasDoji)
		{
			// Bullish breakout above doji high with SMA confirmation
			if (candle.ClosePrice > _prevHigh && candle.ClosePrice > sma && Position <= 0)
			{
				if (Position < 0)
					BuyMarket();
				BuyMarket();
			}
			// Bearish breakout below doji low with SMA confirmation
			else if (candle.ClosePrice < _prevLow && candle.ClosePrice < sma && Position >= 0)
			{
				if (Position > 0)
					SellMarket();
				SellMarket();
			}
		}

		_prevHigh = candle.HighPrice;
		_prevLow = candle.LowPrice;
		_prevWasDoji = isDoji;
		_hasPrev = true;
	}
}