Стратегия переносит классический советник MetaTrader «Forex Profit System» в высокоуровневый API StockSharp. Она использует три
экспоненциальные скользящие средние по медианной цене свечи (EMA 10, 25 и 50) вместе с фильтром Parabolic SAR. Такое сочетание
позволяет ловить кратковременные импульсы, возникающие в момент, когда быстрая средняя пробивает медленную, а Parabolic SAR уже
успел перейти на сторону нового тренда.
Логика торговли
Индикаторы
Для всех индикаторов используется медианная цена завершённой свечи, что повторяет режим PRICE_MEDIAN в MetaTrader.
Быстрая EMA (10) реагирует на краткосрочный импульс.
Средняя EMA (25) и медленная EMA (50) задают фильтр направления.
Parabolic SAR со шагом 0.02 и максимумом 0.2 подтверждает факт пробоя цены.
Вход в длинную позицию
EMA(10) выше EMA(25) и EMA(50).
На предыдущей закрытой свече EMA(10) находилась ниже EMA(50), то есть произошёл «пробой вверх».
Значение Parabolic SAR располагается ниже цены закрытия свечи.
Открытых позиций нет и торговля разрешена (стратегия подключена и не заблокирована по лимитам).
Вход в короткую позицию
EMA(10) ниже EMA(25) и EMA(50).
На предыдущей свече EMA(10) была выше EMA(50) — подтверждение пробоя вниз.
Parabolic SAR расположен выше цены закрытия.
Сопровождение позиции
Сразу после открытия выставляются жёсткие стоп-лосс и тейк-профит с отдельными настройками для покупок и продаж.
Как только цена проходит заданное расстояние в прибыльную сторону, включается трейлинг-стоп, подтягивающий защиту к текущей
цене на фиксированное количество пунктов.
Дополнительный ранний выход происходит, если EMA(10) разворачивается в обратную сторону (для лонга — опускается ниже своего
предыдущего значения, для шорта — поднимается выше), и накопленная прибыль превышает минимальный порог.
Значения параметров по умолчанию
Параметр
Значение
Описание
CandleType
Таймфрейм 15 минут
Тип свечей, которые обрабатывает стратегия.
FastEmaLength
10
Период быстрой EMA.
MediumEmaLength
25
Период средней EMA.
SlowEmaLength
50
Период медленной EMA.
SarStep
0.02
Начальный шаг Parabolic SAR.
SarMax
0.2
Максимальный шаг Parabolic SAR.
Volume
0.1
Торговый объём в лотах/контрактах.
LongTakeProfitPoints
50
Размер тейк-профита для покупок (в пунктах).
ShortTakeProfitPoints
50
Размер тейк-профита для продаж (в пунктах).
LongStopLossPoints
30
Размер стоп-лосса для покупок (в пунктах).
ShortStopLossPoints
30
Размер стоп-лосса для продаж (в пунктах).
LongTrailingStopPoints
10
Дистанция включения трейлинг-стопа для покупок.
ShortTrailingStopPoints
10
Дистанция включения трейлинг-стопа для продаж.
LongProfitTriggerPoints
10
Минимальная прибыль (в пунктах) для закрытия лонга по развороту EMA.
ShortProfitTriggerPoints
5
Минимальная прибыль (в пунктах) для закрытия шорта по развороту EMA.
Особенности реализации
Используются подписки на свечи и связывание индикаторов через высокоуровневый API StockSharp, что избавляет от необходимости
работать с низкоуровневой книгой заявок.
Все дистанции управления риском переводятся из пунктов в реальные ценовые величины с помощью PriceStep. Если шаг цены не
задан, стратегия применяет указанные значения напрямую, чтобы не терять работоспособность.
Защитные уровни (SetStopLoss, SetTakeProfit) выставляются на итоговую позицию, получающуюся после отправки рыночного ордера,
что учитывает возможные частичные исполнения.
Хранится цена последнего входа по каждой стороне, благодаря чему трейлинг и выход по EMA рассчитывают фактический прогресс.
Обработка ведётся только на завершённых свечах, поэтому стратегия не «перерисовывается» и повторяет поведение исходного советника
MetaTrader, работавшего в функции start().
Рекомендации по применению
Наиболее комфортно стратегия работает на ликвидных валютных парах во внутридневном режиме (базовый таймфрейм — 15 минут).
Для инструментов с иным шагом цены или волатильностью подберите подходящие значения пунктовых параметров (StopLoss,
TakeProfit, TrailingStop, ProfitTrigger).
При работе на площадках с расширяющимся спредом рассмотрите добавление фильтров по сессиям или спреду: стратегия ориентирована на
относительно узкий спред и быстрое исполнение.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class ForexProfitSystemStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast; private decimal _prevSlow; private bool _hasPrev;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ForexProfitSystemStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 10).SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 25).SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = default;
_prevSlow = default;
_hasPrev = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) return;
if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }
if (_cooldown > 0)
{
_cooldown--;
_prevFast = fast; _prevSlow = slow;
return;
}
if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_cooldown = 2;
}
else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_cooldown = 2;
}
_prevFast = fast; _prevSlow = slow;
}
}