Стратегия Follow Line является переносом советника MetaTrader FollowLineEA_v1.0. Она использует связку индикаторов Bollinger Bands, ATR и набора простых скользящих средних, чтобы построить динамическую линию тренда. Линия скользит только в направлении прорыва, поэтому образует ступенчатую структуру и помогает удерживать позицию до завершения импульса.
Логика торговли
Цепочка индикаторов
Полосы Боллинджера с параметрами BollingerPeriod и BollingerDeviations определяют факт выхода цены за диапазон.
ATR (AtrPeriod) опционально расширяет линию, если активирован флаг UseAtrFilter.
Набор SMA (MovingAveragePeriod) по High, Low, Open, Close и медиане формирует подтверждение сигналов в режимах OpenCloseMedian и HighLowOpenClose.
Обновление линии
Закрытие выше верхней полосы поднимает линию под минимум свечи (с учётом ATR), но не позволяет ей опуститься ниже предыдущего значения.
Закрытие ниже нижней полосы опускает линию над максимум свечи и тоже не даёт ей подняться выше предыдущего уровня.
Знак приращения линии определяет состояние тренда: >0 — бычий, <0 — медвежий.
Сигналы
Переключение линии из отрицательного наклона в положительный даёт стрелку на покупку при выполнении фильтра по стрелкам.
Переключение из положительного наклона в отрицательный формирует стрелку на продажу.
Параметр IndicatorsShift задерживает исполнение сигнала на указанное число свечей, что повторяет обращение к буферу индикатора в MQL.
Фильтры
Временной фильтр (UseTimeFilter, TimeStartTrade, TimeEndTrade). Окно может пересекать полуночь.
Контроль спреда (MaxSpread) и максимального объёма (MaxOrders).
Стопы: стоп-лосс, тейк-профит, трейлинг и безубыток (UseStopLoss, UseTakeProfit, UseTrailingStop, UseBreakEven). Все расстояния указываются в шагах цены.
Лотность: AutoLotSize распределяет долю капитала (RiskFactor), иначе используется фиксированный ManualLotSize. Объём нормируется по шагу и биржевым ограничениям.
Параметры
Сохранены все настройки оригинала: таймфрейм (CandleType), глубина истории (BarsCount), параметры индикаторов, фильтры времени и спреда, блок управления позицией и денежным менеджментом.
Примечания по использованию
Стратегия работает по закрытым свечам, поэтому результаты тестов и реальной торговли совпадают при одинаковой компрессии данных.
TypeOfArrows = HideArrows полностью отключает торговлю, оставляя только индикацию.
Визуализация сделок включена через CreateChartArea() и DrawOwnTrades().
Особенности конвертации
Реализация выполнена на высокоуровневом API StockSharp без обращения к индикаторным буферам через GetValue.
Управление ордерами повторяет оригинальные вызовы BuyMarket/SellMarket и защитные стопы.
Все комментарии в коде на английском языке согласно требованиям репозитория.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Follow Line Trend strategy: EMA + Momentum trend follower.
/// Buys when close > EMA and momentum > 100.
/// Sells when close < EMA and momentum < 100.
/// </summary>
public class FollowLineTrendStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _momPeriod;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
public int MomPeriod
{
get => _momPeriod.Value;
set => _momPeriod.Value = value;
}
public FollowLineTrendStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_emaPeriod = Param(nameof(EmaPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA period", "Indicators");
_momPeriod = Param(nameof(MomPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Momentum", "Momentum period", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var mom = new Momentum { Length = MomPeriod };
decimal? prevClose = null;
decimal? prevEma = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, mom, (candle, emaVal, momVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
if (prevClose.HasValue && prevEma.HasValue)
{
var crossUp = prevClose.Value <= prevEma.Value && close > emaVal;
var crossDown = prevClose.Value >= prevEma.Value && close < emaVal;
if (crossUp && momVal > 100m && Position <= 0)
BuyMarket();
else if (crossDown && momVal < 100m && Position >= 0)
SellMarket();
}
prevClose = close;
prevEma = emaVal;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
}
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, Momentum
from StockSharp.Algo.Strategies import Strategy
class follow_line_trend_strategy(Strategy):
def __init__(self):
super(follow_line_trend_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 14) \
.SetDisplay("EMA Period", "EMA period", "Indicators")
self._mom_period = self.Param("MomPeriod", 10) \
.SetDisplay("Momentum", "Momentum period", "Indicators")
self._ema = None
self._mom = None
self._prev_close = None
self._prev_ema = None
@property
def ema_period(self):
return self._ema_period.Value
@property
def mom_period(self):
return self._mom_period.Value
def OnReseted(self):
super(follow_line_trend_strategy, self).OnReseted()
self._ema = None
self._mom = None
self._prev_close = None
self._prev_ema = None
def OnStarted2(self, time):
super(follow_line_trend_strategy, self).OnStarted2(time)
self._ema = ExponentialMovingAverage()
self._ema.Length = self.ema_period
self._mom = Momentum()
self._mom.Length = self.mom_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(30)))
subscription.Bind(self._ema, self._mom, self._process_candle)
subscription.Start()
def _process_candle(self, candle, ema_value, mom_value):
if candle.State != CandleStates.Finished:
return
if not self._ema.IsFormed or not self._mom.IsFormed:
return
close = float(candle.ClosePrice)
ema_val = float(ema_value)
mom_val = float(mom_value)
if self._prev_close is not None and self._prev_ema is not None:
cross_up = self._prev_close <= self._prev_ema and close > ema_val
cross_down = self._prev_close >= self._prev_ema and close < ema_val
if cross_up and mom_val > 100.0 and self.Position <= 0:
self.BuyMarket()
elif cross_down and mom_val < 100.0 and self.Position >= 0:
self.SellMarket()
self._prev_close = close
self._prev_ema = ema_val
def CreateClone(self):
return follow_line_trend_strategy()