В данной папке находится порт советника MetaTrader «Fractals Martingale», реализованный на высокоуровневом API StockSharp.
Алгоритм объединяет фракталы Билла Вильямса, трендовый фильтр на основе индикатора Ишимоку и подтверждение по месячному MACD.
Размер позиции регулируется по принципу мартингейла: после убыточных сделок объем следующей заявки умножается на коэффициент, а
защитная пауза предотвращает бесконтрольный рост экспозиции.
Логика торговли
Поиск фракталов на рабочем таймфрейме. Стратегия хранит историю завершенных свечей и ищет локальные экстремумы, окруженные
минимум FractalDepth барами. Если следующая свеча открывается выше верхнего фрактала, формируется бычий сигнал; если ниже
нижнего фрактала – медвежий. Найденные уровни остаются актуальными в течение FractalLookback обработанных свечей.
Фильтр Ишимоку. Сигнал учитывается только тогда, когда на старшем таймфрейме (IchimokuCandleType) линия Tenkan расположена
выше Kijun для покупок и ниже Kijun для продаж.
Подтверждение MACD. Как и в исходном советнике, применяется месячный MACD (серия MacdCandleType, по умолчанию 30-дневные
свечи). Длинные позиции открываются, только если линия MACD находится выше сигнальной, а короткие – если ниже.
Фильтр торговой сессии. Заявки размещаются лишь в диапазоне часов от StartHour (включительно) до EndHour (не включая).
Поддерживается режим с переходом через полночь.
Мартингейл. Базовый объем задается параметром TradeVolume. После убыточной серии следующий объем умножается на
Multiplier и приводится к шагу объема инструмента. При прибыли цепочка сбрасывается. При превышении MaxConsecutiveLosses
торговля приостанавливается на PauseMinutes минут и затем возобновляется с базовым объемом.
Переворот позиции. Перед открытием нового ордера стратегия закрывает встречный объем, чтобы в портфеле не оставалось
разнонаправленных позиций.
Управление рисками
StopLossPips и TakeProfitPips переводятся в абсолютное изменение цены с учетом шага пункта и передаются в StartProtection.
Это повторяет настройки исходного советника, где стопы задавались в пунктах.
Денежный трейлинг-стоп оригинала заменен на встроенный защитный механизм StockSharp, поскольку работа с валютой счета зависит
от брокера.
Параметры
Параметр
Описание
TradeVolume
Базовый объем первой заявки в серии.
Multiplier
Во сколько раз увеличить объем после убыточной сделки.
StopLossPips, TakeProfitPips
Расстояние до стоп-лосса и тейк-профита в пунктах.
FractalDepth
Количество баров по обе стороны при определении фрактала.
FractalLookback
Сколько обработанных свечей хранить фрактал в качестве актуального уровня.
StartHour, EndHour
Торговые часы (по времени площадки). Равные значения отключают фильтр.
MaxConsecutiveLosses
Допустимое число подряд идущих убыточных сделок перед паузой.
PauseMinutes
Длительность защитной паузы в минутах.
TenkanPeriod, KijunPeriod, SenkouPeriod
Периоды линий Ишимоку на старшем таймфрейме.
MacdFastPeriod, MacdSlowPeriod, MacdSignalPeriod
Периоды EMA для фильтра MACD.
CandleType
Основная серия свечей для сигналов и сделок.
IchimokuCandleType
Таймфрейм для расчета Ишимоку.
MacdCandleType
Таймфрейм для MACD (по умолчанию приблизительный месяц).
Рекомендации по использованию
Расчет пункта. Значение пункта берется из Security.PriceStep; для пятизначных форекс-котировок производится умножение на 10,
чтобы соответствовать логике MetaTrader.
Множественные подписки. Стратегии требуются до трех разных серий свечей. Убедитесь, что поставщик данных поддерживает
необходимые таймфреймы.
Контроль мартингейла. Умножение объема резко увеличивает риски. Используйте параметры Multiplier, MaxConsecutiveLosses
и PauseMinutes, чтобы адаптировать стратегию к капиталу.
Отличия от версии MT4. В переводе отсутствуют почтовые и push-уведомления, денежные трейлинг-стопы и явные проверки маржи –
эти функции реализованы средствами StockSharp. Логика входа/выхода и наращивания позиции сохранена.
Содержимое
CS/FractalsMartingaleStrategy.cs – реализация стратегии на C#.
README.md – документация на английском языке.
README_zh.md – описание на китайском языке.
README_ru.md – описание на русском языке (данный файл).
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;
public class FractalsMartingaleStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public FractalsMartingaleStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class fractals_martingale_strategy(Strategy):
def __init__(self):
super(fractals_martingale_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(fractals_martingale_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(fractals_martingale_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return fractals_martingale_strategy()