MA Reverse — это порт на StockSharp простого советника MetaTrader 4 «MA_Reverse». Исходный робот отслеживает, как долго цена
Bid находится выше или ниже простой скользящей средней (SMA) с периодом 14. После достаточно долгой серии баров в одну сторону
он открывает позицию в расчёте на возврат цены к среднему значению. В версии для StockSharp та же идея реализована через
подсчёт количества завершённых свечей, закрывающихся выше или ниже SMA, и немедленное исполнение рыночного ордера, когда
срабатывает заданный порог.
Торговая логика
Стратегия подписывается на свечи выбранного таймфрейма и рассчитывает простую скользящую среднюю с периодом SmaPeriod.
Поддерживается целочисленный счётчик (его целевое значение задаётся параметром StreakThreshold), который увеличивается,
пока цена закрытия остаётся выше средней, и уменьшается, пока цена закрытия ниже. При касании средней счётчик сбрасывается.
Когда счётчик достигает StreakThreshold, а цена закрытия минимум на MinimumDeviation выше SMA, отправляется рыночный ордер
на продажу. Предполагается, что затянувшееся движение вверх относительно средней скоро развернётся.
При достижении -StreakThreshold и отклонении закрытия на MinimumDeviation ниже SMA логика зеркально открывает длинную
позицию.
После совершения сделки счётчик не обнуляется автоматически — он продолжает отслеживать следующую серию, как и в исходном
советнике.
Управление ордерами
Рыночные входы используют объём TradeVolume. Если на момент сигнала существует противоположная позиция, стратегия сначала
закрывает её и в том же ордере открывает новую позицию, что повторяет поведение MetaTrader при переводе из лонга в шорт или
наоборот.
Глобальный тейк-профит настраивается через вспомогательный метод StartProtection. Расстояние вычисляется как TakeProfitPoints
умноженное на шаг цены инструмента и повторяет выражение «30 * Point» из MQL-кода. При достижении цели позиция закрывается
рыночным ордером.
В оригинальном советнике не предусмотрен стоп-лосс, поэтому в порте он также отсутствует. Управление рисками полностью
лежит на тейк-профите и настройках риск-менеджмента пользователя.
Параметры
Параметр
Описание
TradeVolume
Лот для каждого рыночного входа. Это же значение используется при развороте позиции для закрытия старых сделок и открытия новых.
SmaPeriod
Количество свечей в расчёте простой скользящей средней. По умолчанию соответствует 14, как в советнике.
StreakThreshold
Сколько последовательных закрытий должно оказаться по одну сторону от SMA, прежде чем разрешается открытие позиции.
MinimumDeviation
Минимальное абсолютное расстояние между ценой закрытия и SMA, подтверждающее экстремум.
TakeProfitPoints
Дистанция тейк-профита в шагах цены. Умножается на PriceStep, чтобы получить абсолютное значение.
CandleType
Тип свечей (таймфрейм), используемый для расчёта SMA и оценки серий.
Примечания
Счётчик работает на завершённых свечах, полученных через SubscribeCandles, что делает реализацию устойчивой и совместимой с
прогоном по историческим данным. Поведение совпадает с тиковым вариантом MetaTrader при достаточно мелком таймфрейме.
StockSharp агрегирует позиции, поэтому несколько последовательных сделок рассматриваются как одна позиция с единым плавающим
тейк-профитом. Это аналогично постановке одинакового тейк-профита на каждую заявку в MetaTrader.
Индикатор не добавляется в Strategy.Indicators, поскольку привязка через Bind сама управляет жизненным циклом индикатора.
Перед запуском стратегии уточняйте шаг цены и торговый объём для конкретных инструментов у брокера, чтобы TakeProfitPoints
давал ожидаемое абсолютное смещение.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Moving average reversal strategy converted from the MetaTrader MA_Reverse expert advisor.
/// Counts how many consecutive closes remain on one side of the SMA and opens a trade once the streak is long enough.
/// </summary>
public class MaReverseStrategy : Strategy
{
private readonly StrategyParam<int> _smaPeriod;
private readonly StrategyParam<int> _streakThreshold;
private readonly StrategyParam<decimal> _minimumDeviation;
private readonly StrategyParam<DataType> _candleType;
private int _streak;
public MaReverseStrategy()
{
_smaPeriod = Param(nameof(SmaPeriod), 14)
.SetDisplay("SMA Period", "Number of candles used by the moving average.", "Indicator");
_streakThreshold = Param(nameof(StreakThreshold), 3)
.SetDisplay("Streak Threshold", "Number of consecutive closes required before reversing.", "Logic");
_minimumDeviation = Param(nameof(MinimumDeviation), 0.0001m)
.SetDisplay("Minimum Deviation", "Minimum distance between price and SMA to confirm the reversal.", "Logic");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe used for SMA calculation.", "General");
}
public int SmaPeriod
{
get => _smaPeriod.Value;
set => _smaPeriod.Value = value;
}
public int StreakThreshold
{
get => _streakThreshold.Value;
set => _streakThreshold.Value = value;
}
public decimal MinimumDeviation
{
get => _minimumDeviation.Value;
set => _minimumDeviation.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_streak = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_streak = 0;
var sma = new SimpleMovingAverage { Length = SmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
var closePrice = candle.ClosePrice;
var deviation = closePrice - smaValue;
if (deviation == 0m)
{
_streak = 0;
return;
}
if (deviation > 0m)
{
// Price above SMA
if (_streak < 0)
_streak = 0;
_streak++;
if (_streak >= StreakThreshold && deviation > MinimumDeviation)
{
// Long streak above SMA => sell (reversal)
if (Position >= 0)
{
SellMarket();
_streak = 0;
}
}
}
else
{
// Price below SMA
if (_streak > 0)
_streak = 0;
_streak--;
if (-_streak >= StreakThreshold && -deviation > MinimumDeviation)
{
// Long streak below SMA => buy (reversal)
if (Position <= 0)
{
BuyMarket();
_streak = 0;
}
}
}
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ma_reverse_strategy(Strategy):
"""
MA reversal: counts consecutive closes on one side of SMA.
After streak threshold, opens a reversal trade.
"""
def __init__(self):
super(ma_reverse_strategy, self).__init__()
self._sma_period = self.Param("SmaPeriod", 14).SetDisplay("SMA Period", "SMA period", "Indicators")
self._streak_threshold = self.Param("StreakThreshold", 3).SetDisplay("Streak", "Consecutive closes needed", "Logic")
self._min_deviation = self.Param("MinDeviation", 0.0001).SetDisplay("Min Deviation", "Min distance from SMA", "Logic")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
self._streak = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ma_reverse_strategy, self).OnReseted()
self._streak = 0
def OnStarted2(self, time):
super(ma_reverse_strategy, self).OnStarted2(time)
sma = SimpleMovingAverage()
sma.Length = self._sma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, sma_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
sma = float(sma_val)
deviation = close - sma
if deviation == 0:
self._streak = 0
return
if deviation > 0:
if self._streak < 0:
self._streak = 0
self._streak += 1
if self._streak >= self._streak_threshold.Value and deviation > self._min_deviation.Value:
if self.Position >= 0:
self.SellMarket()
self._streak = 0
else:
if self._streak > 0:
self._streak = 0
self._streak -= 1
if -self._streak >= self._streak_threshold.Value and -deviation > self._min_deviation.Value:
if self.Position <= 0:
self.BuyMarket()
self._streak = 0
def CreateClone(self):
return ma_reverse_strategy()