Farhad Crab — это порт оригинального советника MetaTrader FarhadCrab1.mq4 на высокоуровневый API StockSharp. Изначально алгоритм предназначался для минутных графиков пар GBP/JPY, GBP/USD и EUR/USD и комбинировал агрессивные входы по движению с динамическим сопровождением позиций. В новой реализации вся логика переписана на C#, а обмен данными происходит исключительно через подписки StockSharp.
В рабочем таймфрейме стратегия рассчитывает две скользящие средние: 9-периодную EMA от типичной цены и 9-периодную SMA от цен открытия свечи. Дополнительно ведётся дневная 55-периодная сглаженная средняя (SMMA), служащая «страховкой» от глобального разворота. Если минимум текущей свечи держится выше EMA и позиция отсутствует, открывается покупка. Если максимум остаётся ниже SMA от открытий, открывается продажа. Пересечение дневной SMMA с ценой закрытия в противоположную сторону немедленно закрывает позицию.
Система управления выходами повторяет настройки советника: отдельные значения тейк-профита (в пунктах) для длинных и коротких сделок, а также независимые параметры трейлинг-стопа. Трейлинг активируется только после движения цены на заданную величину, как и в MetaTrader. Закрытия выполняются рыночными заявками, что соответствует событиям высокоуровневого API.
Ключевые особенности
Полное соответствие индикаторной базе — EMA(9) по типичной цене, SMA(9) по ценам открытия и дневная SMMA(55).
Работа с несколькими таймфреймами — одновременная подписка на торговый и дневной таймфреймы без ручного накопления данных.
Настраиваемые выходы — тейк-профиты и трейлинг-стопы в пунктах, доступные для оптимизации.
Дневной предохранитель — строгая реализация условия закрытия при пересечении дневной SMMA и цены закрытия.
Защита позиций — однократный вызов StartProtection() в момент запуска стратегии.
Параметры
Параметр
Описание
Значение по умолчанию
OrderVolume
Объём сделки при открытии рынка.
0.1
LongTakeProfitPips
Дистанция тейк-профита для покупок в пунктах.
10
ShortTakeProfitPips
Дистанция тейк-профита для продаж в пунктах.
10
LongTrailingStopPips
Дистанция трейлинг-стопа для покупок (0 отключает).
8
ShortTrailingStopPips
Дистанция трейлинг-стопа для продаж (0 отключает).
8
DailyMaPeriod
Период дневной сглаженной средней.
55
CandleType
Основной таймфрейм стратегии (по умолчанию 1 минута).
1m
Все параметры объявлены через StrategyParam<T> и помечены для оптимизации там, где это имеет смысл, поэтому стратегию легко адаптировать под конкретный инструмент.
Правила торговли
Открытие лонга: минимум свечи находится выше EMA(9) по типичной цене, позиция отсутствует.
Открытие шорта: максимум свечи находится ниже SMA(9) по ценам открытия, позиция отсутствует.
Защитное закрытие лонга: дневная SMMA закрывается выше цены закрытия при условии, что ранее была ниже.
Защитное закрытие шорта: дневная SMMA закрывается ниже цены закрытия при условии, что ранее была выше.
Тейк-профит: достижение целевого количества пунктов закрывает позицию.
Трейлинг-стоп: после движения в прибыль на заданное расстояние отслеживается обратный ход и при откате позиция закрывается.
Особенности реализации
Вся обработка данных выполняется через SubscribeCandles().Bind(...), что исключает прямой доступ к буферам индикаторов и соответствует внутренним правилам репозитория.
Размер пункта вычисляется на основании PriceStep с учётом пятого и третьего знаков, как в MetaTrader, поэтому параметры в пунктах работают идентично.
Стоп-приказы не выставляются, вместо этого стратегия контролирует цену и закрывает позиции рыночными ордерами при наступлении условий.
Метод OnReseted очищает все поля, благодаря чему повторные запуски и оптимизации выполняются из чистого состояния.
Рекомендации по использованию
Для повторения исходных результатов используйте минутные свечи по основным валютным парам. Тем не менее параметры позволяют адаптироваться к другим инструментам и волатильности.
Стратегия всегда держит только одну позицию, что упрощает мониторинг и анализ сделок.
В условиях бокового рынка можно сократить расстояние трейлинг-стопа или сосредоточиться на тейк-профите, чтобы уменьшить число ложных выходов.
Увеличение 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()