在 GitHub 上查看
E-Friday 会话策略
概述
E-Friday 策略复刻了经典的 MetaTrader 专家顾问,它只在星期五交易。策略观察上一日的日线蜡烛,并在设定的时间于星期五开盘时入场。方向采取反向思路:若上一日收于开盘价下方(阴线),则在星期五做多;若上一日收于开盘价上方(阳线),则在星期五做空。仓位仅在当日管理,可在预设时间后自动平仓,也可通过保护性止损退出。
交易规则
- 订阅日线蜡烛(默认:1 天)以获得前一日的开盘价和收盘价。
- 在星期五订阅日内蜡烛(默认:1 分钟)以检测指定的入场小时。
- 当出现入场小时的第一根蜡烛时:
- 若上一日为阴线则做多。
- 若上一日为阳线则做空。
- 若上一日开收相等则放弃交易。
- 到达设定的平仓小时后,可选择自动平掉当日仓位。
- 使用止损、止盈以及可选的跟踪止损来管理仓位,跟踪逻辑包含原始 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;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class e_friday_session_strategy(Strategy):
def __init__(self):
super(e_friday_session_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20).SetDisplay("EMA Period", "EMA trend filter", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_open = 0.0
self._prev_close = 0.0
self._has_prev = False
@property
def ema_period(self): return self._ema_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(e_friday_session_strategy, self).OnReseted()
self._prev_open = 0.0; self._prev_close = 0.0; self._has_prev = False
def OnStarted2(self, time):
super(e_friday_session_strategy, self).OnStarted2(time)
self._has_prev = False
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, self.process_candle).Start()
def process_candle(self, candle, ema):
if candle.State != CandleStates.Finished: return
close = float(candle.ClosePrice)
ema_val = float(ema)
if not self._has_prev:
self._prev_open = float(candle.OpenPrice); self._prev_close = close; self._has_prev = True; return
prev_bearish = self._prev_close < self._prev_open
prev_bullish = self._prev_close > self._prev_open
if prev_bearish and close > ema_val and self.Position <= 0:
if self.Position < 0: self.BuyMarket()
self.BuyMarket()
elif prev_bullish and close < ema_val and self.Position >= 0:
if self.Position > 0: self.SellMarket()
self.SellMarket()
self._prev_open = float(candle.OpenPrice); self._prev_close = close
def CreateClone(self): return e_friday_session_strategy()