在 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) |
执行流程
- 启动时检查目标品种是否属于加密货币,若是则直接跳过整个流程,与原始 EA 保持一致。
- 创建蜡烛订阅,以便在每根蜡烛完成时获得回调。
- 每当蜡烛收盘时执行时间判断,仅当其为周五且收盘时间不早于截止时刻才继续下一步。
- 从投资组合中读取目标品种的持仓信息,获取浮动盈亏数值。
- 若浮盈大于零,则提交相反方向的市价单,彻底平掉对应仓位。
- 在下单前检查是否已有同方向的活动平仓单,以防止重复提交。
使用提示
- 请在非加密货币的品种上运行本策略,并确保所选投资组合中包含需要保护的头寸。
- 策略不会开立新仓,仅作为风险控制模块管理已有持仓。
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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Momentum, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class close_profit_end_of_week_strategy(Strategy):
def __init__(self):
super(close_profit_end_of_week_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._mom_period = self.Param("MomPeriod", 20)
self._ema_period = self.Param("EmaPeriod", 50)
self._momentum_level = self.Param("MomentumLevel", 101.0)
self._prev_mom = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def MomPeriod(self):
return self._mom_period.Value
@MomPeriod.setter
def MomPeriod(self, value):
self._mom_period.Value = value
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
@property
def MomentumLevel(self):
return self._momentum_level.Value
@MomentumLevel.setter
def MomentumLevel(self, value):
self._momentum_level.Value = value
def OnReseted(self):
super(close_profit_end_of_week_strategy, self).OnReseted()
self._prev_mom = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(close_profit_end_of_week_strategy, self).OnStarted2(time)
self._prev_mom = 0.0
self._has_prev = False
mom = Momentum()
mom.Length = self.MomPeriod
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(mom, ema, self._process_candle).Start()
def _process_candle(self, candle, mom_value, ema_value):
if candle.State != CandleStates.Finished:
return
mom_val = float(mom_value)
ema_val = float(ema_value)
close = float(candle.ClosePrice)
level = float(self.MomentumLevel)
if self._has_prev:
if self._prev_mom <= level and mom_val > level and close > ema_val and self.Position <= 0:
self.BuyMarket()
elif self._prev_mom >= (200.0 - level) and mom_val < (200.0 - level) and close < ema_val and self.Position >= 0:
self.SellMarket()
self._prev_mom = mom_val
self._has_prev = True
def CreateClone(self):
return close_profit_end_of_week_strategy()