在 GitHub 上查看

周末盈利平仓策略

概述

Close Profit End Of Week Strategy 对 MetaTrader 脚本 Closeprofitendofweek.mq5 进行了移植。该策略监控指定交易品种,在每周五到达可配置的截止时间之后,将所有盈利头寸通过市价单平掉,以避免周末跳空造成的收益回吐。

原始 MQL 行为

原始 EA 通过计时器循环检查交易服务器时间。当时间为周五且达到设定的结束时间时,它遍历当前品种的所有持仓,对每一个正浮盈的订单发送平仓指令。若品种属于加密货币,则会跳过逻辑,因为这类市场周末不停盘。

StockSharp 实现

C# 版本在 StockSharp 高层 API 上复现了相同的保护流程:

  • 订阅可配置的蜡烛序列,仅用于获取稳定的时间戳。
  • 仅在蜡烛完成时触发检查,并确认该蜡烛属于周五且收盘时间晚于截止时刻。
  • 读取关联投资组合中目标品种的持仓及其浮动盈亏。
  • 对每个仍处于盈利状态的头寸提交反向市价单,实现即时平仓。
  • 如果检测到目标品种是加密资产,则完全停用该保护机制。

参数

名称 说明 默认值
StartTradeTime 监控窗口的开始时间(保留原 MQL 输入,当前逻辑未使用)。 00:00
EndTradeTime 每周五在该时间之后关闭所有盈利头寸。 20:00
CloseTradesAtEndTime 是否启用自动平仓功能。 true
CandleType 用于驱动时间检查的数据类型(默认 1 分钟 K 线)。 TimeFrameCandle(1m)

执行流程

  1. 启动时检查目标品种是否属于加密货币,若是则直接跳过整个流程,与原始 EA 保持一致。
  2. 创建蜡烛订阅,以便在每根蜡烛完成时获得回调。
  3. 每当蜡烛收盘时执行时间判断,仅当其为周五且收盘时间不早于截止时刻才继续下一步。
  4. 从投资组合中读取目标品种的持仓信息,获取浮动盈亏数值。
  5. 若浮盈大于零,则提交相反方向的市价单,彻底平掉对应仓位。
  6. 在下单前检查是否已有同方向的活动平仓单,以防止重复提交。

使用提示

  • 请在非加密货币的品种上运行本策略,并确保所选投资组合中包含需要保护的头寸。
  • 策略不会开立新仓,仅作为风险控制模块管理已有持仓。
  • StartTradeTime 仅用于兼容原始参数或未来扩展,目前流程不会引用该值。
  • 如果需要同时管理多个品种,请像在 MetaTrader 中一样为每个品种单独运行一个实例。

限制

  • 策略依赖券商投资组合的实时浮盈数据,若数据延迟,平仓指令也会滞后。
  • 只会处理目标品种的头寸,其他品种的仓位保持不变。
  • 逻辑在蜡烛收盘时执行,如需更精细的时间控制,请选择更短的蜡烛周期。
namespace StockSharp.Samples.Strategies;

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

/// <summary>
/// Close Profit End Of Week strategy: Momentum + EMA trend following.
/// Buys when momentum positive and close above EMA, sells when momentum negative and close below EMA.
/// </summary>
public class CloseProfitEndOfWeekStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _momPeriod;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<decimal> _momentumLevel;

	private decimal _prevMom;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int MomPeriod { get => _momPeriod.Value; set => _momPeriod.Value = value; }
	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public decimal MomentumLevel { get => _momentumLevel.Value; set => _momentumLevel.Value = value; }

	public CloseProfitEndOfWeekStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_momPeriod = Param(nameof(MomPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Momentum Period", "Momentum period", "Indicators");
		_emaPeriod = Param(nameof(EmaPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA filter period", "Indicators");
		_momentumLevel = Param(nameof(MomentumLevel), 101m)
			.SetDisplay("Momentum Level", "Momentum threshold", "Signals");
	}

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

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevMom = 0;
		_hasPrev = false;
		var mom = new Momentum { Length = MomPeriod };
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(mom, ema, ProcessCandle).Start();
	}

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

		if (_hasPrev)
		{
			if (_prevMom <= MomentumLevel && momValue > MomentumLevel && candle.ClosePrice > emaValue && Position <= 0)
				BuyMarket();
			else if (_prevMom >= 200m - MomentumLevel && momValue < 200m - MomentumLevel && candle.ClosePrice < emaValue && Position >= 0)
				SellMarket();
		}

		_prevMom = momValue;
		_hasPrev = true;
	}
}