Использует пересечение стохастика с настраиваемыми параметрами %K, %D и замедлением для генерации сигналов.
Реализует мартингейловую сетку: добавляет позиции как по направлению прибыли, так и при усреднении против движения.
Рассчитана на неттинг – стратегия удерживает только одну суммарную длинную или короткую позицию.
Логика работы
Условия входа
Обработка ведётся по закрытым свечам таймфрейма CandleType.
Значения стохастика берутся с предыдущей завершённой свечи, что соответствует вызову MQL iStochastic(..., 1).
Вход в лонг выполняется, если:
Предыдущее значение %K выше %D.
Предыдущее %D превышает BuyLevel.
Открытых позиций нет.
Вход в шорт выполняется, если:
Предыдущее значение %K ниже %D.
Предыдущее %D ниже SellLevel.
Открытых позиций нет.
Все рыночные ордера используют объём Volume, нормализованный по Security.VolumeStep.
Масштабирование позиции
ProfitPips задаёт дистанцию в пипсах, по достижении которой добавляется ещё один базовый объём по тренду.
Для лонга: если максимум свечи достигает lastEntryPrice + ProfitPips * positionCount, отправляется ордер с базовым Volume.
Для шорта: если минимум свечи достигает lastEntryPrice - ProfitPips * positionCount, отправляется базовый ордер на продажу.
StepPips определяет расстояние для мартингейлового усреднения против движения.
Для лонга: при снижении минимума до lastEntryPrice - StepPips следующий объём равен lastVolume * Multiplier.
Для шорта: при росте максимума до lastEntryPrice + StepPips используется тот же множитель.
После каждой сделки обновляются lastEntryPrice, lastVolume и счётчик открытых позиций.
Выход из позиции
Хранится цена последнего входа по текущему направлению.
При достижении lastEntryPrice ± ProfitPips (по максимумам для лонга и минимумам для шорта) все позиции закрываются рыночным ордером.
После обнуления позиции внутреннее состояние мартингейла сбрасывается.
Параметры
Параметр
Значение по умолчанию
Описание
Volume
0.03
Базовый лот для стартового входа и добавлений по прибыли.
Multiplier
1.6
Множитель для мартингейлового усреднения.
StepPips
25
Расстояние в пипсах для усредняющих заявок против тренда.
ProfitPips
9
Дистанция в пипсах для фиксации прибыли и добавления по тренду.
KPeriod
5
Период расчёта %K стохастика.
DPeriod
3
Период сглаживания линии %D.
Slowing
3
Дополнительное сглаживание %K (slow stochastic).
BuyLevel
20
Минимальное значение %D для разрешения лонга.
SellLevel
55
Максимальное значение %D для разрешения шорта.
CandleType
таймфрейм 5 минут
Таймфрейм для построения свечей и индикатора.
Особенности реализации
Размер пипса вычисляется из Security.PriceStep. Для инструментов с 3 или 5 знаками цена шага дополнительно умножается на 10, что повторяет логику MQL.
Объёмы округляются вниз до ближайшего Security.VolumeStep. Если результат меньше минимального шага объёма, ордер не отправляется.
Проверки условий выполняются по максимумам и минимумам свечи, чтобы приблизить внутрисвечное поведение исходного советника при работе с высокоуровневым API.
Метод OnOwnTradeReceived фиксирует фактические цены и объёмы, поддерживая корректную последовательность мартингейловых шагов.
Рекомендации по использованию
Выставляйте CandleType в соответствии с таймфреймом исходного шаблона MetaTrader (обычно M5), чтобы получить схожую динамику.
Убедитесь, что у инструмента корректно заданы шаг цены и объёма, иначе подстройте Volume, StepPips и ProfitPips вручную.
Дополнительно применяйте внешние лимиты по риску: мартингейл увеличивает позицию при неблагоприятном движении.
Отличия от оригинала
В StockSharp стратегия работает по закрытым свечам, а не по каждому тику; проверки выполняются по high/low свечи.
Проверка свободной маржи, доступная в MetaTrader, в данной реализации отсутствует и должна контролироваться отдельно.
Используется неттинговая модель StockSharp; режим хеджирования не поддерживается.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Martingale Expert strategy (simplified). Uses RSI oscillator with
/// martingale-style position scaling and profit targets.
/// </summary>
public class MartingailExpertStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<decimal> _buyLevel;
private readonly StrategyParam<decimal> _sellLevel;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
public decimal BuyLevel
{
get => _buyLevel.Value;
set => _buyLevel.Value = value;
}
public decimal SellLevel
{
get => _sellLevel.Value;
set => _sellLevel.Value = value;
}
public MartingailExpertStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "Indicators");
_buyLevel = Param(nameof(BuyLevel), 35m)
.SetDisplay("Buy Level", "RSI level for longs", "Logic");
_sellLevel = Param(nameof(SellLevel), 65m)
.SetDisplay("Sell Level", "RSI level for shorts", "Logic");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = RsiLength };
decimal prevRsi = 50;
var hasPrev = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, (ICandleMessage candle, decimal rsiVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!hasPrev)
{
prevRsi = rsiVal;
hasPrev = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
prevRsi = rsiVal;
return;
}
// RSI crosses up from oversold
if (prevRsi < BuyLevel && rsiVal >= BuyLevel && Position <= 0)
BuyMarket();
// RSI crosses down from overbought
else if (prevRsi > SellLevel && rsiVal <= SellLevel && Position >= 0)
SellMarket();
prevRsi = rsiVal;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var rsiArea = CreateChartArea();
if (rsiArea != null)
DrawIndicator(rsiArea, rsi);
}
}
}
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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class martingail_expert_strategy(Strategy):
def __init__(self):
super(martingail_expert_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candles", "General")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period", "Indicators")
self._buy_level = self.Param("BuyLevel", 35.0) \
.SetDisplay("Buy Level", "RSI level for longs", "Logic")
self._sell_level = self.Param("SellLevel", 65.0) \
.SetDisplay("Sell Level", "RSI level for shorts", "Logic")
self._prev_rsi = 50.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiLength(self):
return self._rsi_length.Value
@property
def BuyLevel(self):
return self._buy_level.Value
@property
def SellLevel(self):
return self._sell_level.Value
def OnReseted(self):
super(martingail_expert_strategy, self).OnReseted()
self._prev_rsi = 50.0
self._has_prev = False
def OnStarted2(self, time):
super(martingail_expert_strategy, self).OnStarted2(time)
self._prev_rsi = 50.0
self._has_prev = False
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_value)
if not self._has_prev:
self._prev_rsi = rv
self._has_prev = True
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_rsi = rv
return
if self._prev_rsi < self.BuyLevel and rv >= self.BuyLevel and self.Position <= 0:
self.BuyMarket()
elif self._prev_rsi > self.SellLevel and rv <= self.SellLevel and self.Position >= 0:
self.SellMarket()
self._prev_rsi = rv
def CreateClone(self):
return martingail_expert_strategy()