在 GitHub 上查看

E-Friday 会话策略

概述

E-Friday 策略复刻了经典的 MetaTrader 专家顾问,它只在星期五交易。策略观察上一日的日线蜡烛,并在设定的时间于星期五开盘时入场。方向采取反向思路:若上一日收于开盘价下方(阴线),则在星期五做多;若上一日收于开盘价上方(阳线),则在星期五做空。仓位仅在当日管理,可在预设时间后自动平仓,也可通过保护性止损退出。

交易规则

  1. 订阅日线蜡烛(默认:1 天)以获得前一日的开盘价和收盘价。
  2. 在星期五订阅日内蜡烛(默认:1 分钟)以检测指定的入场小时。
  3. 当出现入场小时的第一根蜡烛时:
    • 若上一日为阴线则做多。
    • 若上一日为阳线则做空。
    • 若上一日开收相等则放弃交易。
  4. 到达设定的平仓小时后,可选择自动平掉当日仓位。
  5. 使用止损、止盈以及可选的跟踪止损来管理仓位,跟踪逻辑包含原始 EA 中的盈利触发和最小移动距离约束。

实现说明

  • 使用 StockSharp 的高级蜡烛订阅机制,同时获取日线背景与日内时序信号。
  • 将 MQL 版本中的“点数”风险参数换算为基于合约最小价格步长的绝对价格距离。
  • 在代码中维护跟踪止损:每根已完成的蜡烛都会更新止损位置,并在价格穿越止损或目标时发送市价单平仓。
  • 通过日内状态记录,确保每个星期五仅开一次仓位。
  • 支持多空双向,与原始 EA 一样,每个策略实例只操作单一品种。

参数

名称 说明 默认值
Volume 交易手数或合约数量。 0.1
StopLossPoints 止损距离(价格步长单位,0 为关闭)。 75
TakeProfitPoints 止盈距离(价格步长单位,0 为关闭)。 0
HourOpen 入场小时(0-23)。 7
UseClosePositions 是否在设定时间后自动平仓。 true
HourClose 自动平仓的小时(0-23)。 19
UseTrailing 是否启用跟踪止损。 true
ProfitTrailing 是否要求利润超过跟踪距离后再启动跟踪。 true
TrailingStopPoints 跟踪止损距离(价格步长单位)。 60
TrailingStepPoints 每次收紧跟踪止损所需的额外距离。 5
IntradayCandleType 日内信号所用的蜡烛类型(默认 1 分钟)。 TimeSpan.FromMinutes(1)
DailyCandleType 日线背景所用的蜡烛类型(默认 1 天)。 TimeSpan.FromDays(1)

使用建议

  • 请确保品种的交易时段与星期五的入场小时对齐,以便在市场开盘后及时入场。
  • 设定止损和跟踪距离时,请使用与标的最小价格步长相同的“点数”定义,以还原 MetaTrader 的行为。
  • 策略设计为每个星期五仅交易一次,如需同时交易多个品种,请为每个品种单独运行一个策略实例。

与原始 EA 的差异

  • 策略使用已完成蜡烛的收盘数据,而原始 EA 在每个 tick 上检查时间条件。
  • 当蜡烛最高/最低突破目标时,通过市价单平仓,可能与逐 tick 跟踪存在细微差异。
  • 参数通过 StockSharp StrategyParam 系统暴露,可用于优化与界面展示。
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>
/// E-Friday Session strategy - candle body direction with EMA filter.
/// Buys when previous candle was bearish and current closes above EMA (reversal).
/// Sells when previous candle was bullish and current closes below EMA (reversal).
/// </summary>
public class EFridaySessionStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevOpen;
	private decimal _prevClose;
	private bool _hasPrev;

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

	public EFridaySessionStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 20)
			.SetDisplay("EMA Period", "EMA trend filter", "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(); _prevOpen = 0m; _prevClose = 0m; _hasPrev = false; }

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

		_hasPrev = false;

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };

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

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

		var close = candle.ClosePrice;

		if (!_hasPrev)
		{
			_prevOpen = candle.OpenPrice;
			_prevClose = close;
			_hasPrev = true;
			return;
		}

		var prevBearish = _prevClose < _prevOpen;
		var prevBullish = _prevClose > _prevOpen;

		// Previous bearish + close above EMA = buy reversal
		if (prevBearish && close > ema && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Previous bullish + close below EMA = sell reversal
		else if (prevBullish && close < ema && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevOpen = candle.OpenPrice;
		_prevClose = close;
	}
}