在 GitHub 上查看

Daily Target 策略

概述

DailyTargetStrategy 复刻了 MetaTrader 4 顾问程序 “Daily Target”。策略实时统计当日的浮动盈亏与已实现盈亏之和, 一旦达到设定的盈利目标或者跌破允许的最大亏损,就会立即撤销所有挂单并用市价单平掉仓位,直到次日才允许继续交易。

运行逻辑

  1. 启动阶段
    • OnStarted 中调用 ResetDailySnapshot 记录当前日期以及当天开始时的已实现盈亏。
    • 通过 SubscribeLevel1() 获取最新的买一/卖一报价,以便精确计算浮动盈亏。
    • SubscribeTrades() 用来保存最近成交价,在缺少报价时作为备用价格来源。
    • 设定 1 分钟定时器,即使市场静止也能及时发现日期切换。
  2. 盈亏评估
    • EvaluateDailyThresholds 将当前 PnL 与基准值作差得到当日已实现盈亏,并叠加根据最新报价(或成交价)计算出的浮动盈亏。
    • 当总盈亏达到盈利目标或跌至最大亏损阈值以下时,调用 TriggerDailyStop 执行紧急处理。
  3. 紧急退出
    • TriggerDailyStop 写入提示日志、取消全部未成交订单,并根据持仓方向发送对冲市价单以平仓。
    • _dailyStopTriggered 标记可防止当日内再次入场;日期发生变化时,ResetDailySnapshot 会清除此标记并重新记录盈亏基准。

参数

名称 默认值 说明
DailyTarget 10 以账户货币计价的日内盈利目标;总盈亏达到该值后,当日停止交易。
DailyMaxLoss 0 允许的最大日内亏损(账户货币)。设置为 0 可关闭该限制;当总盈亏低于负阈值时停止交易。

其他说明

  • 策略仅管理分配给该实例的主交易品种,与原版 MQL 顾问的单品种模式一致。
  • 浮动盈亏计算时,多头使用买价 Bid,空头使用卖价 Ask;若缺少报价,则回退到最近成交价。
  • 本目录只包含 C# 高阶 API 实现,暂不提供 Python 版本。
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Daily Target strategy: TEMA crossover.
/// Buys when fast TEMA crosses above slow TEMA, sells on cross below.
/// </summary>
public class DailyTargetStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }

	public DailyTargetStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast TEMA", "Fast TEMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow TEMA", "Slow TEMA period", "Indicators");
	}

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

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = 0;
		_prevSlow = 0;
		_hasPrev = false;
		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, ProcessCandle).Start();
	}

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

		if (_hasPrev)
		{
			if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
				BuyMarket();
			else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
				SellMarket();
		}
		else
		{
			if (fastValue > slowValue && Position <= 0)
				BuyMarket();
			else if (fastValue < slowValue && Position >= 0)
				SellMarket();
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
		_hasPrev = true;
	}
}