在 GitHub 上查看
Farhad Crab 策略
概述
Farhad Crab 策略 是 MetaTrader 专家顾问 FarhadCrab1.mq4 的 StockSharp 高级 API 移植版。原始 EA 主要用于 M1 周期的 GBP/JPY、GBP/USD 与 EUR/USD,依靠快速的顺势突破与跟踪止损管理。本移植版本将其核心思想转换为 C#,结合短周期均线滤波、日线平滑均线安全阀以及自动化的止盈/移动止损流程。
策略在主交易周期同时计算两条均线:基于典型价的 9 周期 EMA 与基于开盘价的 9 周期 SMA。除此之外,还会在日线周期维护一条 55 周期的平滑移动平均线(SMMA)。当短周期条件显示价格强劲位于 EMA 之上且当前没有持仓时,策略建立多头;当最高价保持在开盘价 SMA 之下时,策略建立空头。日线 SMMA 用作风险控制:若其从下方上穿日线收盘价,则强制平掉所有多头;若从上方下穿,则平掉所有空头。
在离场管理方面,策略提供与原 EA 相同的可配置参数:以点数表示的多头/空头止盈距离,以及独立的移动止损触发。移动止损只有在价格领先一定距离后才会被激活,与 MetaTrader 实现保持一致。所有平仓动作均以市价指令完成,以适配 StockSharp 的事件驱动模型。
核心特性
- 与原始 EA 相同的指标组合:典型价 9 周期 EMA、开盘价 9 周期 SMA、日线 55 周期 SMMA。
- 多时间框架支持:同时订阅交易周期与日线,借助 StockSharp 自动计算指标,无需手动缓存数据。
- 可配置离场:多/空止盈和移动止损都以点数设定,与原始外部输入一致,可用于参数优化。
- 日线安全阀:完全复制 EA 的“日线均线穿越即平仓”规则,防止趋势反转时继续持仓。
- 内置保护:在启动时调用
StartProtection(),遵循项目的风险管理要求。
参数
| 参数 |
描述 |
默认值 |
OrderVolume |
新开仓使用的下单手数。 |
0.1 |
LongTakeProfitPips |
多头止盈距离,单位为点。 |
10 |
ShortTakeProfitPips |
空头止盈距离,单位为点。 |
10 |
LongTrailingStopPips |
多头移动止损距离,设为 0 表示禁用。 |
8 |
ShortTrailingStopPips |
空头移动止损距离,设为 0 表示禁用。 |
8 |
DailyMaPeriod |
日线 SMMA 的周期长度。 |
55 |
CandleType |
主交易周期,默认使用 1 分钟蜡烛。 |
1m |
所有参数均通过 StrategyParam<T> 提供,并在需要时标记为可优化,方便在 StockSharp 优化器中批量测试。
交易规则
- 多头入场:当前蜡烛的最低价高于典型价 EMA,且当前无持仓时,开多。
- 空头入场:当前蜡烛的最高价低于开盘价 SMA,且当前无持仓时,开空。
- 多头保护性离场:当日线 SMMA 由下向上穿过日线收盘价(且之前在其下方)时,平掉所有多头。
- 空头保护性离场:当日线 SMMA 由上向下穿过日线收盘价(且之前在其上方)时,平掉所有空头。
- 止盈:价格达到设定的点数目标后立即平仓。
- 移动止损:价格向有利方向推进超过设定距离后开始跟踪,若回撤超过该距离则市价平仓。
实现细节
- 代码完全依赖高层的
SubscribeCandles().Bind(...) 调用,不读取指标缓冲区,符合项目规范。
- 点值根据证券的
PriceStep 计算,并对 3/5 位报价做 MetaTrader 风格的调整,以保证行为一致。
- 止盈与移动止损通过监控价格并在触发时市价平仓完成,避免挂出额外的止损/止盈委托,更适合异步成交环境。
- 在
OnReseted 中清空全部内部状态,确保多次启动或优化运行时不会遗留历史数据。
使用建议
- 原策略针对高波动货币对的 M1 周期进行了优化。若希望保持类似表现,可使用默认周期并结合外汇主要货币对,当然也可以调整参数适配其它市场。
- 策略始终保持单一持仓,便于回测与实盘监控,无需处理网格或加仓逻辑。
- 若市场震荡幅度较大,可适当减小移动止损距离或仅依赖止盈离场,以避免频繁被“摇出”。
- 日线 SMMA 是主要的风险控制工具,如希望更平滑的趋势过滤,可增加
DailyMaPeriod 的取值。
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>
/// Farhad Crab strategy - EMA and SMA crossover with trend filter.
/// Buys when EMA crosses above SMA.
/// Sells when EMA crosses below SMA.
/// </summary>
public class FarhadCrabStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _smaPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevEma;
private decimal _prevSma;
private bool _hasPrev;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int SmaPeriod { get => _smaPeriod.Value; set => _smaPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public FarhadCrabStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 10)
.SetDisplay("EMA Period", "EMA lookback", "Indicators");
_smaPeriod = Param(nameof(SmaPeriod), 20)
.SetDisplay("SMA Period", "SMA lookback", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(10).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevEma = 0m; _prevSma = 0m; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var sma = new SimpleMovingAverage { Length = SmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, sma, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema, decimal sma)
{
if (candle.State != CandleStates.Finished)
return;
if (!_hasPrev)
{
_prevEma = ema;
_prevSma = sma;
_hasPrev = true;
return;
}
if (_prevEma <= _prevSma && ema > sma && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (_prevEma >= _prevSma && ema < sma && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevEma = ema;
_prevSma = sma;
}
}
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, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class farhad_crab_strategy(Strategy):
"""
Farhad Crab strategy - EMA and SMA crossover with trend filter.
Buys when EMA crosses above SMA, sells when EMA crosses below SMA.
"""
def __init__(self):
super(farhad_crab_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 10) \
.SetDisplay("EMA Period", "EMA lookback", "Indicators")
self._sma_period = self.Param("SmaPeriod", 20) \
.SetDisplay("SMA Period", "SMA lookback", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(10))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_ema = 0.0
self._prev_sma = 0.0
self._has_prev = False
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(farhad_crab_strategy, self).OnReseted()
self._prev_ema = 0.0
self._prev_sma = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(farhad_crab_strategy, self).OnStarted2(time)
self._has_prev = False
ema = ExponentialMovingAverage()
ema.Length = self._ema_period.Value
sma = SimpleMovingAverage()
sma.Length = self._sma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, sma, self._process_candle).Start()
def _process_candle(self, candle, ema_val, sma_val):
if candle.State != CandleStates.Finished:
return
ema_val = float(ema_val)
sma_val = float(sma_val)
if not self._has_prev:
self._prev_ema = ema_val
self._prev_sma = sma_val
self._has_prev = True
return
if self._prev_ema <= self._prev_sma and ema_val > sma_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev_ema >= self._prev_sma and ema_val < sma_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_ema = ema_val
self._prev_sma = sma_val
def CreateClone(self):
return farhad_crab_strategy()