Стратегия реагирует на сильные бычьи и медвежьи свечи и открывает позиции по направлению движения. Для длинных и коротких сделок ведутся независимые цепочки мартингейла: убыток по лонгу увеличивает следующий объём через параметр Bull Multiplier, убыток по шорту использует Bear Multiplier. Расстояния стоп-лосса и тейк-профита также задаются отдельно для каждого направления, что полностью повторяет возможности исходного MQL советника.
Логика работы
Подписка на выбранный тип свечей (по умолчанию 1 минута) и обработка только закрытых свечей.
При отсутствии позиции:
Бычий сигнал:Close > Open и тело свечи превышает фильтр для покупок — выполняется рыночная покупка.
Медвежий сигнал:Close < Open и тело больше порога для продаж — выполняется рыночная продажа.
Для каждой сделки выставляются стоп-лосс и тейк-профит, преобразованные из пунктов в цену согласно шагу стоимости инструмента.
После закрытия позиции вычисляется реализованный результат относительно сохранённой базы:
Отрицательный результат увеличивает объём следующей сделки в соответствующей цепочке мартингейла.
Положительный или нулевой результат возвращает объём к начальному значению.
Пока позиция открыта, новые сигналы игнорируются, что обеспечивает поведение «одна сделка за раз», как в оригинальном роботе.
Управление капиталом
Цепочки мартингейла для длинных и коротких сделок не пересекаются, поэтому серия убытков по одной стороне не влияет на другую.
Объём автоматически приводится к допустимому шагу VolumeStep, чтобы избежать отклонения заявок.
Вызов StartProtection(useMarketOrders: true) активирует встроенную защиту StockSharp для сопроводительных стопов и тейков.
Параметры
Параметр
Описание
Initial Volume
Стартовый объём для обеих мартингейл-цепочек.
Bull Multiplier
Множитель объёма для следующей длинной сделки после убытка.
Bear Multiplier
Множитель объёма для следующей короткой сделки после убытка.
Bull Stop Loss
Расстояние стоп-лосса в пунктах для лонгов (конвертируется в цену).
Bull Take Profit
Расстояние тейк-профита в пунктах для лонгов.
Bear Stop Loss
Расстояние стоп-лосса в пунктах для шортов.
Bear Take Profit
Расстояние тейк-профита в пунктах для шортов.
Bull Body Filter
Минимальный размер тела бычьей свечи в пунктах для открытия лонга.
Bear Body Filter
Минимальный размер тела медвежьей свечи в пунктах для открытия шорта.
Candle Type
Таймфрейм, на котором формируются сигналы (по умолчанию 1 минута).
Рекомендации по использованию
Убедитесь, что инструмент предоставляет корректные PriceStep и VolumeStep. При отсутствии PriceStep стратегия использует значение 0.0001.
Логика мартингейла опирается на реализованный PnL, поэтому даже ручное закрытие позиций корректно обновляет цепочки.
Для оптимизации имеет смысл подбирать сочетания фильтров по телу свечи и множителей, чтобы найти баланс между частотой входов и глубиной просадки.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Bull/Bear Candle Martingale strategy: Bullish/bearish candle direction + EMA filter.
/// Buys after strong bullish candle when close crosses above EMA.
/// Sells after strong bearish candle when close crosses below EMA.
/// </summary>
public class BullBearCandleMartingaleStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
public BullBearCandleMartingaleStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_emaPeriod = Param(nameof(EmaPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA period", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
decimal? prevClose = null;
decimal? prevEma = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, (candle, emaVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var bullish = close > candle.OpenPrice;
var bearish = close < candle.OpenPrice;
var bodySize = Math.Abs(close - candle.OpenPrice);
var range = candle.HighPrice - candle.LowPrice;
// Require strong candle body (>50% of range)
var strongCandle = range > 0 && bodySize / range > 0.5m;
if (prevClose.HasValue && prevEma.HasValue && strongCandle)
{
var crossUp = prevClose.Value <= prevEma.Value && close > emaVal;
var crossDown = prevClose.Value >= prevEma.Value && close < emaVal;
if (bullish && crossUp && Position <= 0)
BuyMarket();
else if (bearish && crossDown && 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
from StockSharp.Algo.Strategies import Strategy
class bull_bear_candle_martingale_strategy(Strategy):
def __init__(self):
super(bull_bear_candle_martingale_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 30) \
.SetDisplay("EMA Period", "EMA period", "Indicators")
self._ema = None
self._prev_close = None
self._prev_ema = None
@property
def ema_period(self):
return self._ema_period.Value
def OnReseted(self):
super(bull_bear_candle_martingale_strategy, self).OnReseted()
self._ema = None
self._prev_close = None
self._prev_ema = None
def OnStarted2(self, time):
super(bull_bear_candle_martingale_strategy, self).OnStarted2(time)
self._ema = ExponentialMovingAverage()
self._ema.Length = self.ema_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromHours(1)))
subscription.Bind(self._ema, self._process_candle)
subscription.Start()
def _process_candle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
if not self._ema.IsFormed:
return
close = float(candle.ClosePrice)
open_p = float(candle.OpenPrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
ema_val = float(ema_value)
bullish = close > open_p
bearish = close < open_p
body_size = abs(close - open_p)
range_size = high - low
strong_candle = range_size > 0 and body_size / range_size > 0.5
if self._prev_close is not None and self._prev_ema is not None and strong_candle:
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 bullish and cross_up and self.Position <= 0:
self.BuyMarket()
elif bearish and cross_down and self.Position >= 0:
self.SellMarket()
self._prev_close = close
self._prev_ema = ema_val
def CreateClone(self):
return bull_bear_candle_martingale_strategy()